首页 文章

为什么来自套接字的recv()不会阻塞

提问于
浏览
0

我创建一个TCP服务器套接字,侦听connect()请求和accept()它们 . 接受客户端套接字后,从中接收数据 . 我希望recv()函数以超时阻塞,但它似乎是非阻塞的 .

我从服务器接收响应时已经使客户端处于阻塞模式,但它似乎与服务器端的工作方式不同 .

下面我在阻塞模式下复制粘贴我想要接收数据的服务器端:

int clientSocket = accept(_serverSocket, (struct sockaddr *)NULL, NULL);

if (clientSocket < 0)
    return -1;

// set TIMEOUT option to server socket
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500 * 1000; 
int sockOptRet = setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

struct linger sl;
sl.l_onoff = 1;     /* non-zero value enables linger option in kernel */
sl.l_linger = 0;    /* timeout interval in seconds */
setsockopt(clientSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));

char _rcvBuffer[sizeof(can_frame)];
int numBytesRcv = recv(clientSocket, _rcvBuffer, sizeof(can_frame), 0);

我也尝试过MSG_WAITALL标志,但它没有改变任何东西......

1 回答

  • 0

    你的代码应该明确阻止,但你的0.5s的超时并没有显示正义 .

    如果你将超时增加到更明显的东西(例如2s)并将代码移植到例如beej's tpc server template上,你会得到类似的东西:

    /*
    ** server.c -- a stream socket server demo
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <sys/wait.h>
    #include <signal.h>
    
    #define PORT "3490"  // the port users will be connecting to
    
    #define BACKLOG 10     // how many pending connections queue will hold
    
    void sigchld_handler(int s)
    {
        // waitpid() might overwrite errno, so we save and restore it:
        int saved_errno = errno;
    
        while(waitpid(-1, NULL, WNOHANG) > 0);
    
        errno = saved_errno;
    }
    
    
    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa)
    {
        if (sa->sa_family == AF_INET) {
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }
    
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    int main(void)
    {
        int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
        struct addrinfo hints, *servinfo, *p;
        struct sockaddr_storage their_addr; // connector's address information
        socklen_t sin_size;
        struct sigaction sa;
        int yes=1;
        char s[INET6_ADDRSTRLEN];
        int rv;
    
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_PASSIVE; // use my IP
    
        if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
            return 1;
        }
    
        // loop through all the results and bind to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next) {
            if ((sockfd = socket(p->ai_family, p->ai_socktype,
                    p->ai_protocol)) == -1) {
                perror("server: socket");
                continue;
            }
    
            if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                    sizeof(int)) == -1) {
                perror("setsockopt");
                exit(1);
            }
    
            if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
                close(sockfd);
                perror("server: bind");
                continue;
            }
    
            break;
        }
    
        freeaddrinfo(servinfo); // all done with this structure
    
        if (p == NULL)  {
            fprintf(stderr, "server: failed to bind\n");
            exit(1);
        }
    
        if (listen(sockfd, BACKLOG) == -1) {
            perror("listen");
            exit(1);
        }
    
        sa.sa_handler = sigchld_handler; // reap all dead processes
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
            perror("sigaction");
            exit(1);
        }
    
        printf("server: waiting for connections...\n");
    
        while(1) {  // main accept() loop
            sin_size = sizeof their_addr;
            new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
            if (new_fd == -1) {
                perror("accept");
                continue;
            }
    
            inet_ntop(their_addr.ss_family,
                get_in_addr((struct sockaddr *)&their_addr),
                s, sizeof s);
            printf("server: got connection from %s\n", s);
    
            if (!fork()) { // this is the child process
                close(sockfd); // child doesn't need the listener
    
    
                // set TIMEOUT option to server socket
                struct timeval tv;
                tv.tv_sec = 2;
                tv.tv_usec = 0 * 500 * 1000; 
                int sockOptRet = setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
    
                struct linger sl;
                sl.l_onoff = 1;     /* non-zero value enables linger option in kernel */
                sl.l_linger = 0;    /* timeout interval in seconds */
                setsockopt(new_fd, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));
    
                char can_frame[1024];
                char _rcvBuffer[sizeof(can_frame)];
                int numBytesRcv = recv(new_fd, _rcvBuffer, sizeof(can_frame), 0); 
    
    
    
                if (send(new_fd, "Hello, world!", 13, 0) == -1)
                    perror("send");
                close(new_fd);
                exit(0);
            }
            close(new_fd);  // parent doesn't need this
        }
    
        return 0;
    }
    

    现在如果你运行这个然后做:

    nc localhost 3490
    

    没有键入要发送的行,在服务器放弃之前应该有一个明显的2秒等待,表明 recv 确实是阻塞的 .

相关问题