write (2) по-прежнему сообщает об успехе, хотя я закрыл сокет - PullRequest
0 голосов
/ 06 августа 2010

Я столкнулся с проблемой, когда даже после того, как моя серверная сторона close () - редактировала сокет, клиентская сторона все еще может выполнить одну запись (), только вторая запись () вернет SIGPIPE, это приводит к потере данныхпоскольку у меня нет рукопожатия на уровне приложения, и я полагаюсь исключительно на возвращаемое значение write ().Есть ли способ для меня получить SIGPIPE сразу после того, как сервер закрыл соединение?

Тестовая программа следующая:

Я ожидаю, что 2-я запись вернет SIGPIPE, но она вернула успех,только 3-я запись возвращает SIGPIPE!Почему это?

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdarg.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>

void log_error(const char *fmt, ...) 
{
 va_list ap;
 char buf[BUFSIZ] = {0};

 va_start(ap, fmt);
 vsnprintf(buf, BUFSIZ, fmt, ap);
 fprintf(stderr, "ERROR: %s\n", buf);
 va_end(ap);
 return;
}

static int create_inet_socket()
{
    int fd;
    struct sockaddr_in my_addr;
    int yes = 1;

    if ((fd=socket(PF_INET, SOCK_STREAM, 0))==-1) {
 log_error("create_inet_socket:socket:%d:%s", errno, strerror(errno));
 return -1;
    }

    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))==-1) {
 log_error("create_inet_socket:setsockopt:%d:%s", errno, strerror(errno));
 return -1;
    }

    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(9998);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    memset(&(my_addr.sin_zero), '0', 8);

    if (bind(fd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr_in))==-1) {
 log_error("main:bind:%d:%s", errno, strerror(errno));
 return -1;
    }

    if (listen(fd, 5)==-1) {
 log_error("main:listen:%d:%s", errno, strerror(errno));
 return -1;
    }

    return fd;
}

int myconnect()
{
    int fd;
    char ch[1] = {'a'};
    struct sockaddr_in sin;
    fd_set wfds;
    struct timeval tv;
    struct hostent *he;
    ssize_t nwritten;

    if ((fd=socket(PF_INET, SOCK_STREAM, 0))==-1) {
 log_error("rlog:socket failed:%d:%s", errno, strerror(errno));
 return -1;
    }

    bzero(&sin, sizeof(sin));

    if ((he=gethostbyname("localhost"))==NULL) {
 log_error("rlog:gethostbyname failed:%d:%s", errno, strerror(errno));
 return -1;
    }

    sin.sin_addr = *((struct in_addr*)he->h_addr);
    sin.sin_port = htons(9998);
    sin.sin_family = AF_INET;

    if (connect(fd,(struct sockaddr *) &sin,sizeof(sin)) == -1) {
 log_error("connect:%d:%s", errno, strerror(errno));
 return 0;
    }

    nwritten = write(fd, &ch, 1);
    if (nwritten==-1) {
 log_error("write:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "client : 1. written %ld\n", nwritten);
    }
    sleep(3);
    nwritten = write(fd, &ch, 1);
    if (nwritten==-1) {
 log_error("write:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "client : 2. written %ld\n", nwritten);
    }
    sleep(3);
    nwritten = write(fd, &ch, 1);
    if (nwritten==-1) {
 log_error("write:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "client : 3. written %ld\n", nwritten);
    }
    return 0;
}

void run_server()
{
    int fd;
    int newfd;
    char c[1];
    ssize_t nread;
    int status;
    fprintf(stderr, "server : Running\n");
    fd = create_inet_socket();
    if (fd==-1) {
 perror("create_inet_socket");
    }

    newfd = accept(fd, NULL, NULL);
    fprintf(stderr, "server : accepted newfd %d\n", newfd);

    nread = read(newfd, &c, 1);
    if (nread==-1) {
 log_error("read:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "read returned %ld, closing socket\n", nread);
    }
    shutdown(newfd, SHUT_RDWR);
    close(newfd);
    wait(&status);
    fprintf(stderr, "server : exit\n");
}

void run_client()
{
    fprintf(stderr, "client : running\n");
    myconnect();
    fprintf(stderr, "client : exit\n");
    _exit(1);
}

int main()
{
    signal(SIGPIPE, SIG_IGN);
    int pid = fork();
    switch (pid) {
    case 0:
 run_client();
 break;
    case -1:
 perror("fork");
 break;
    default:
 run_server();
 break;
    }
    return 0;
}

1 Ответ

4 голосов
/ 06 августа 2010

Вы не можете полагаться только на возвращаемое значение write(2) - это большое условие состязания между двумя концами соединения (ядро кэширует данные, которые вы передаете системным вызовам, пакетам требуется время для пересечения провода и т. Д.)и, таким образом, вводит возможность потери данных в случае разрыва соединения транспортного уровня.Если вам нужна надежность, спроектируйте протокол уровня приложения так, чтобы принимающая сторона подтверждала все данные.

Раньше была хорошая статья о чем-то вроде этого - Последняя страница SO_LINGER или почему мой TCP не поддерживаетсянадежный , но в данный момент кажется, что он не работает.Google этот заголовок, он может быть отражен где-то.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...