2

我的代码是:

int main(int argc, char *argv[])
{
    int sockfd, new_fd;  /* listen on sock_fd, new connection on new_fd */
    struct sockaddr_in my_addr;    /* my address information */
    struct sockaddr_in their_addr; /* connector's address information */
    socklen_t sin_size;

    /* generate the socket */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    /* generate the end point */
    my_addr.sin_family = AF_INET;         /* host byte order */
    my_addr.sin_port = htons(MYPORT);     /* short, network byte order */
    my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
        /* bzero(&(my_addr.sin_zero), 8);   ZJL*/     /* zero the rest of the struct */

    /* bind the socket to the end point */
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
    == -1) {
        perror("bind");
        exit(1);
    }

    /* start listnening */
    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    printf("server starts listnening %d...\n",sockfd);

    /* repeat: accept, send, close the connection */
    /* for every accepted connection, use a sepetate process or thread to serve it */
while(1) {  /* main accept() loop */
    sin_size = sizeof(struct sockaddr_in);
    if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \
    &sin_size)) == -1) {
        perror("accept");
        continue;
    }
    printf("server: got connection from %s\n", \
        inet_ntoa(their_addr.sin_addr));

    if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) {
    perror("recv");
    exit(1);
    }

    buf[numbytes] = '\0';

    printf("Received: %s",buf);

    if (send(new_fd, "Hello, world!\n", MAXDATASIZE, 0) == -1)
        perror("send");
        close(new_fd);
        exit(0);

    close(new_fd);  /* parent doesn't need this */

    while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */
}
return 0;
}

所以每当我执行这个服务器时,在一个客户端使用它之后它就会终止。但是如果我想在 60 秒内再次执行它,那么它会给出一个错误,bind: Address already in use我认为 close() 函数实际上释放了套接字,以便它可以立即再次使用它。那么我在这里错过了什么?

4

4 回答 4

8

在调用之前bind,您可以使用SO_REUSEADDRsocket 选项标记您希望重用地址/端口:

int reuseaddr = 1;
int err = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
                     &reuseaddr, sizeof(reuseaddr));
于 2012-10-24T00:32:36.213 回答
0

首先,此代码的原始形式来自Beej's guide

您提供的代码要么非常错误,要么为简洁而编辑。发送“Hello World”响应后,您调用 exit(0); 请添加花括号。

if (send(new_fd, "Hello, world!\n", MAXDATASIZE, 0) == -1)
    perror("send");
    close(new_fd);
    exit(0);

Beej;s 代码:

    if (!fork()) { // this is the child process
        close(sockfd); // child doesn't need the listener
        if (send(new_fd, "Hello, world!", 13, 0) == -1)
            perror("send");
        close(new_fd);
        exit(0);
    }
    close(new_fd);  // parent doesn't need this`

我还可以指出,Beej 的代码和您的代码在连接丢失或被客户端中止的情况下不处理“recv”返回 0 的事件。附带说明,请记住对 recv 的调用将阻塞。

if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) {
perror("recv");
exit(1);
}

虽然这似乎不会影响崩溃,但当客户端意外关闭时,此特定问题可能会导致意外崩溃。

于 2012-10-25T06:29:20.843 回答
0

延迟是由于 TIME_WAIT

在终止连接的过程中,需要牢记的重要一点是,连接两端的应用程序进程必须独立关闭它的一半连接。由于 TCP 连接的三次握手策略,内核等待对方的连接也关闭的确认

但是,您可以通过以下方法覆盖此功能:

方法一

在 /etc/sysctl.conf 文件中,添加以下行以在重新启动后保留它:

net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1

方法二

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
于 2013-03-13T18:53:37.180 回答
0

另外,我看不到 BACKLOG 是在哪里定义的,您在 listen() 调用中使用了它。如果这是偶然设置为 1,您可能需要增加它。然后,当最后一个套接字关闭时,您可以处理下一个调用。

于 2012-10-24T01:28:24.167 回答