首次调用recv()后出现"C-传输端点未连接"错误

7
我刚开始学习用C语言进行网络编程。我做了一些测试,但是卡在一个错误上了。
我有一个客户端:
client.c
#include <string.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

int main(void)
{
    struct addrinfo hints, *res;
    int sock;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    getaddrinfo("localhost", "5996", &hints, &res);
    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    connect(sock, res->ai_addr, res->ai_addrlen);
    char data[64];
    int len = 13;
    int br = recv(sock, data, len, 0);
    printf("%s\n%s\n%d\n", strerror(errno), data, br);
    return 0;
}

一个服务器: server.c
#include <string.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MYPORT "5996"
#define BACKLOG 10

int main(void)
{
    struct sockaddr_storage their_addr;
    socklen_t addr_size;
    struct addrinfo hints, *res;
    int sockfd, new_fd;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    getaddrinfo(NULL, MYPORT, &hints, &res);

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    bind(sockfd, res->ai_addr, res->ai_addrlen);
    listen(sockfd, BACKLOG);

    addr_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
    char *msg = "Hello World!!";
    int len = strlen(msg);
    int bs = send(new_fd, msg, len, 0);
    close(sockfd);
}

当我启动服务器时,它会等待连接,如果我启动客户端,我会收到消息“Hello World !!”,但是接下来的一分钟左右,如果我尝试再次运行服务器然后再次运行客户端,则会从strerror()调用中收到消息"Transport endpoint is not connected"
我确实阅读了其他关于此问题的问题,但问题在于数据应该发送到从accept()调用返回的套接字...但我认为这就是我正在做的。 我做错了什么?? ...我知道这是愚蠢的问题,但我是一个真正的初学者。

1
你应该检查connect()的返回值,并在连接失败时查看errno - Werner Henze
谢谢你在帖子中提到的最后一段! :) 我完全忽略了我在代码中做错了这件事! - Horse SMith
1个回答

7
如果每次都在同一端口上重新启动服务器,您可能会发现操作系统仍然将该端口视为正在使用中。这是为了给上一个服务器运行的客户端发送任何未完成请求的交付时间。
您可以通过调用操作码SO_REUSEADDRsetsockopt来覆盖此设置。
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
int reuse = 1;
int err = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
if (0 == err)
    err = bind(sockfd, res->ai_addr, res->ai_addrlen);

请注意上面的代码片段检查错误返回。如果您检查了每个调用的返回代码,您会自己发现问题(即使不知道解决方法)。

类似这样的编程内容:setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));?我应该把它放在哪里? - Sylar
是的,在你创建套接字之后但在调用 bind 之前。我会更新我的答案中的代码,以便更清晰地表达这一点。 - simonc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接