服务器显示在一个非套接字,由于我的多客户端TCP服务器代码中的非套接字上的套接字操作导致罕见但持久的recv()错误...

我有一个TCP服务器和UDP服务器,在同一个端口上,使用select()函数为多个客户端提供服务 . 通常,一切都很好,客户端可以与两个服务器通信,但有时,在极少数情况下,我收到错误消息,这是由于非套接字上的recv()错误套接字操作 . 如果我在这种情况下打印套接字描述符,它通常是1(stdout)或0(stdin) . 所以它应该抛出这个错误,但为什么我的代码首先尝试从那些套接字recv()是我的问题 .

这是我的代码片段

TCP套接字创建

// TCP port setup

int sockfd; // listening socket descriptor

int newsockfd; // newly accept()ed socket descriptor

struct sockaddr_storage remoteaddr; // client address

socklen_t addrlen;

char buf_tcp[256]; // buffer for client data

char buf_copy_tcp[256];

int recv_bytes;

char remoteIP[INET6_ADDRSTRLEN];

int yes=1; // for setsockopt() SO_REUSEADDR

int i, k, rv_getaddrinfo, rv_setsockopt, rv_bind, rv_listen, rv_select;

struct addrinfo hints, *servinfo, *ptr;

// get a socket and bind it

memset(&hints, 0, sizeof(hints));

hints.ai_family = AF_UNSPEC;

hints.ai_socktype = SOCK_STREAM;

hints.ai_flags = AI_PASSIVE;

rv_getaddrinfo = getaddrinfo(NULL, PORT, &hints, &servinfo);

if (rv_getaddrinfo != 0)

{

fprintf(stderr, "CLI Server TCP error: %s\r\n", gai_strerror(rv_getaddrinfo));

exit(1);

}

for(ptr=servinfo; ptr!=NULL; ptr=ptr->ai_next)

{

sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

if (sockfd == -1)

{

fprintf(stdout, "CLI Server TCP error: socket\r\n");

continue;

}

// lose the pesky "address already in use" error message

rv_setsockopt = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

if(rv_setsockopt == -1)

{

fprintf(stdout, "CLI Server TCP error: setsockopt\r\n");

exit(1);

}

rv_bind = bind(sockfd, ptr->ai_addr, ptr->ai_addrlen);

if (rv_bind == -1)

{

close(sockfd);

fprintf(stdout, "CLI Server TCP error: bind\r\n");

continue;

}

break;

}

// if we got here, it means we didn't get bound

if (ptr == NULL)

{

fprintf(stdout, "CLI Server TCP error: failed to bind\r\n");

exit(2);

}

freeaddrinfo(servinfo); // all done with this

// listen

rv_listen = listen(sockfd, 10);

if (rv_listen == -1)

{

fprintf(stdout, "CLI Server TCP error: listen\r\n");

exit(3);

}

UDP套接字创建

// UDP port setup

int sockfd_udp; // listening socket descriptor

struct sockaddr_storage remoteaddr_udp; // client address

socklen_t addrlen_udp;

char buf_udp[256]; // buffer for client data

char buf_copy_udp[256];

int recv_bytes_udp;

char remoteIP_udp[INET6_ADDRSTRLEN];

int yes_udp=1; // for setsockopt() SO_REUSEADDR

int j, rv_getaddrinfo_udp, rv_setsockopt_udp, rv_bind_udp;

struct addrinfo hints_udp, *servinfo_udp, *ptr_udp;

// get a socket and bind it

memset(&hints_udp, 0, sizeof(hints_udp));

hints_udp.ai_family = AF_UNSPEC;

hints_udp.ai_socktype = SOCK_DGRAM;

hints_udp.ai_flags = AI_PASSIVE;

rv_getaddrinfo_udp = getaddrinfo(NULL, PORT, &hints_udp, &servinfo_udp);

if (rv_getaddrinfo_udp != 0)

{

fprintf(stderr, "CLI Server UDP error: %s\r\n", gai_strerror(rv_getaddrinfo_udp));

exit(1);

}

for(ptr_udp=servinfo_udp; ptr_udp!=NULL; ptr_udp=ptr_udp->ai_next)

{

sockfd_udp = socket(ptr_udp->ai_family, ptr_udp->ai_socktype, ptr_udp->ai_protocol);

if (sockfd_udp == -1)

{

fprintf(stdout, "CLI Server UDP error: socket\r\n");

continue;

}

// lose the pesky "address already in use" error message

rv_setsockopt_udp = setsockopt(sockfd_udp, SOL_SOCKET, SO_REUSEADDR, &yes_udp, sizeof(int));

if(rv_setsockopt_udp == -1)

{

fprintf(stdout, "CLI Server UDP error: setsockopt\r\n");

exit(1);

}

rv_bind_udp = bind(sockfd_udp, ptr_udp->ai_addr, ptr_udp->ai_addrlen);

if (rv_bind_udp == -1)

{

close(sockfd_udp);

fprintf(stdout, "CLI Server UDP error: bind\r\n");

continue;

}

break;

}

// if we got here, it means we didn't get bound

if (ptr_udp == NULL)

{

fprintf(stdout, "CLI Server UDP error: failed to bind\r\n");

exit(2);

}

freeaddrinfo(servinfo_udp); // all done with this

3.多客户端服务器的select()函数

struct timeval timeout; // timeout for select(), 1ms

timeout.tv_sec = 0;

timeout.tv_usec = 1000;

fd_set master; // master file descriptor list

fd_set read_fds; // temp file descriptor list for select()

int fdmax; // maximum file descriptor number

FD_ZERO(&master); // clear the master and temp sets

FD_ZERO(&read_fds);

// add the listener to the master set

FD_SET(sockfd, &master);

FD_SET(sockfd_udp, &master);

// keep track of the biggest file descriptor

if(sockfd > sockfd_udp)

fdmax = sockfd; // so far, it's this one

else

fdmax = sockfd_udp; // so far, it's this one

while(1)

{

read_fds = master; // copy it

rv_select = select(fdmax+1, &read_fds, NULL, NULL, &timeout);

if (rv_select == -1)

{

fprintf(stdout, "CLI Server TCP error: select\r\n");

exit(4);

}

// run through the existing connections looking for data to read

for(i=0; i<=fdmax; i++)

{

if (FD_ISSET(i, &read_fds))

{ // we got one!!

if (i == sockfd)

{

// handle new tcp connections

addrlen = sizeof(remoteaddr);

newsockfd = accept(sockfd, (struct sockaddr *)&remoteaddr, &addrlen);

if (newsockfd == -1)

fprintf(stdout, "CLI Server TCP error: accept\r\n");

else

{

FD_SET(newsockfd, &master); // add to master set

if (newsockfd > fdmax) // keep track of the max

fdmax = newsockfd;

inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), remoteIP, INET6_ADDRSTRLEN);

fprintf(stdout, "CLI Server TCP: new connection from %s on socket %d\r\n", remoteIP, newsockfd);

}

}

else if (i == sockfd_udp)

{

// handle new udp connections

addrlen_udp = sizeof(remoteaddr_udp);

if ((recv_bytes_udp = recvfrom(i, buf_udp, sizeof(buf_udp), 0, (struct sockaddr *)&remoteaddr_udp, &addrlen_udp)) < 0)

fprintf(stdout, "CLI Server UDP error: recvfrom\r\n");

else

{ // handle data from a client

inet_ntop(remoteaddr_udp.ss_family, get_in_addr((struct sockaddr*)&remoteaddr_udp), remoteIP_udp, INET6_ADDRSTRLEN);

for(j=0; j<=recv_bytes_udp; j++)

{

if( (buf_udp[j] == '\r') | (buf_udp[j] == '\n') )

buf_udp[j] = '\0';

}

fprintf(stdout, "CLI Server UDP: received %s from connection %s\r\n", buf_udp, remoteIP_udp);

}

}

else

{ // handle data from a client

if ((recv_bytes = recv(i, buf_tcp, sizeof(buf_tcp), 0)) <= 0)

{ // got error or connection closed by client

if (recv_bytes == 0) // connection closed

fprintf(stdout, "CLI Server TCP: socket %d hung up\r\n", i);

else

fprintf(stdout, "CLI Server TCP error: recv from socket %d\r\n", i);

if(i > sockfd_udp) // temporary fix as recv error occurs on any open file descriptors.

close(i); // bye!

FD_CLR(i, &master); // remove from master set

}

else

{

for(k=0; k<=recv_bytes; k++)

{

if( (buf_tcp[k] == '\r') | (buf_tcp[k] == '\n') )

buf_tcp[k] = '\0';

}

fprintf(stdout, "CLI Server TCP: received %s from socket %d\r\n", buf_tcp, i);

}

} // END handle data from client

} // END got new incoming connection

} // END looping through file descriptors

usleep(2000);

}

在select()调用之后,我在for循环中运行所有套接字描述符 . 但if(FD_ISSET())条件应该清除非套接字,如0,1等 . 在while(1)循环中使用FD_SET()的唯一地方是接受()新的TCP客户端,那些套接字描述符必须be> TCP套接字描述符,因此不可能在主集中设置0(stdin),1(stdout) . 但他们不知何故 . 为什么我的代码认为它应该从非套接字recv()然后生成该错误 . 谁在我的select()主集中设置描述符0和1 .

此错误很少见,但仍然存在 . 在我的早期版本的代码中,当我收到错误时,我关闭()该套接字描述符 . 所以我关闭了stdin,stdout和其他一些文件描述符(我用open()创建) . 这搞砸了我的代码所以为了掩盖上面的问题,我使用if(i> sockfd_udp)来关闭()描述符,这样只关闭客户端套接字描述符(通过accept()) .

任何人都可以发现问题吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TCP是一种传输层协议,用于可靠地传输数据。TCP使用套接字(socket)API作为接口,其包括客户端(即“客户端套接字”或“客户端端点”)和服务器(即“服务器套接字”或“服务器端点”)两个组件。 在C语言,可以使用socket API来实现TCP客户端服务器端。客户端套接字可以通过以下步骤进行创建: 1. 创建一个套接字,使用函数socket()来创建,需要指定地址族(AF_INET或AF_INET6)和套接字类型(SOCK_STREAM)。 2. 连接到服务器,使用函数connect()来连接到服务器,需要指定服务器的IP地址和端口号。 3. 发送和接收数据,使用函数send()和recv()来发送和接收数据。 服务器套接字可以通过以下步骤进行创建: 1. 创建一个套接字,使用函数socket()来创建,需要指定地址族(AF_INET或AF_INET6)和套接字类型(SOCK_STREAM)。 2. 绑定到地址,使用函数bind()来将套接字绑定到一个IP地址和端口号。 3. 监听连接,使用函数listen()来监听连接请求。 4. 接受连接,使用函数accept()来接受连接请求,并返回一个新的套接字,用于与客户端进行通信。 5. 发送和接收数据,使用函数send()和recv()来发送和接收数据。 这些步骤可以使用C语言的相应函数来实现,如socket()、connect()、bind()、listen()、accept()、send()和recv()等函数。 ### 回答2: 服务器代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8080 #define BACKLOG 5 int main() { int sockfd, new_sockfd; struct sockaddr_in server_addr, client_addr; socklen_t sin_size; char buffer[1024]; // 创建socket if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } // 设置socket地址结构 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_addr.sin_zero), 8); // 绑定socket和地址 if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } // 监听socket,等待客户端连接 if (listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); } printf("服务器等待客户端连接...\n"); // 接受客户端连接 sin_size = sizeof(struct sockaddr_in); if ((new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size)) == -1) { perror("accept"); exit(1); } printf("客户端 %s 连接成功\n", inet_ntoa(client_addr.sin_addr)); // 接收消息并发送回复 while (1) { memset(buffer, 0, sizeof(buffer)); if ((recv(new_sockfd, buffer, sizeof(buffer), 0)) == -1) { perror("recv"); exit(1); } printf("接收到消息:%s\n", buffer); strcat(buffer, "已收到您的消息"); if ((send(new_sockfd, buffer, strlen(buffer), 0)) == -1) { perror("send"); exit(1); } printf("回复消息:%s\n", buffer); } close(new_sockfd); close(sockfd); return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8080 int main() { int sockfd; struct sockaddr_in server_addr; char buffer[1024]; // 创建socket if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } // 设置socket地址结构 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); bzero(&(server_addr.sin_zero), 8); // 连接服务器 if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } printf("与服务器连接成功,可以发送消息\n"); // 发送消息并接收回复 while (1) { memset(buffer, 0, sizeof(buffer)); printf("请输入要发送的消息:"); fgets(buffer, sizeof(buffer), stdin); if ((send(sockfd, buffer, strlen(buffer), 0)) == -1) { perror("send"); exit(1); } printf("发送消息:%s\n", buffer); memset(buffer, 0, sizeof(buffer)); if ((recv(sockfd, buffer, sizeof(buffer), 0)) == -1) { perror("recv"); exit(1); } printf("收到服务器的回复:%s\n", buffer); } close(sockfd); return 0; } ``` 以上代码实现了一个简单的TCP套接字通信的服务器端和客户端服务器端首先创建一个socket,然后绑定到指定的端口,并监听客户端的连接。当有客户端连接时,接受连接并打印客户端的IP地址。 服务器端接收到客户端发送的消息后,打印接收到的消息内容,并回复一个固定的消息给客户端客户端首先创建一个socket,并连接到服务器指定的IP地址和端口。然后从用户输入获取要发送的消息,将其发送给服务器,并打印发送的消息内容。 客户端接收到服务器的回复消息后,打印收到的回复内容。以上代码可以在本地运行,实现简单的TCP套接字通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值