我有一个在Linux上使用TCP套接字的简单客户端-服务器示例。服务器监听回送地址。客户端连接到服务器,并发送一些整数以及一个“ END”字符串以标记数据结束。服务器读取数字,将所有数字相加并返回总和。但是,read()
即使客户端成功发送了所有数据,我的服务器有时还是处于打开状态。
这是代码:
server.c:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BACKLOG 5
int main(int argc, char *argv[]) {
struct sockaddr_in addr;
int down_flag = 0;
int result = 0;
int ret = 0;
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) {
perror("Create server socket error: %s\n");
return 0;
}
/* Bind socket to loopback address */
memset((void *) &addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == -1) {
perror("Bind server socket failed");
goto _exit;
}
if (listen(sfd, BACKLOG) == -1) {
perror("Listen failed");
goto _exit;
}
ssize_t num_rd = 0;
char buf[100] = {0};
for (;;)
{
printf("Waiting to accept a connection...\n");
int cfd = accept(sfd, NULL, NULL);
printf("Accepted socket fd = %d\n", cfd);
result = 0;
while ((num_rd = read(cfd, buf, sizeof(buf))) > 0) {
/* Ensure the buffer is 0-terminated */
buf[sizeof(buf) - 1] = 0;
printf("Read data: %s\n", buf);
/* Handle commands */
if (!strncmp(buf, "DOWN", sizeof(buf))) {
down_flag = 1;
break;
}
if (!strncmp(buf, "END", sizeof(buf))) {
break;
}
/* Add received summand */
result += atoi(buf);
}
if (-1 == num_rd) {
perror("Read error");
}
/* Send result */
sprintf(buf, "%d", result);
ret = write(cfd, buf, sizeof(buf));
if (-1 == ret) {
perror("Write error\n");
goto _exit;
}
close(cfd);
/* Quit on DOWN command */
if (down_flag) {
break;
}
}
_exit:
close(sfd);
return 0;
}
client.c:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]) {
struct sockaddr_in addr;
int ret;
int data_socket;
char buf[100] = {0};
int i = 0;
data_socket = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == data_socket) {
perror("Create client socket error");
exit(EXIT_FAILURE);
}
/* Connect to server socket */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
ret = connect(data_socket, (const struct sockaddr *) &addr, sizeof(addr));
if (-1 == ret) {
perror("Connect error");
exit(EXIT_FAILURE);
}
/* Send arguments */
for (i = 1; i < argc; i++) {
ret = write(data_socket, argv[i], strlen(argv[i]) + 1);
if (-1 == ret) {
perror("Write error");
break;
}
}
strcpy(buf, "END");
ret = write(data_socket, buf, strlen(buf) + 1);
printf("write %s to socket, ret = %d\n", buf, ret);
if (-1 == ret) {
perror("Write to socket error");
exit(EXIT_FAILURE);
}
/* Read the result */
memset(buf, 0, sizeof(buf));
ret = read(data_socket, buf, sizeof(buf));
if (-1 == ret) {
perror("Read from client socket error");
exit(EXIT_FAILURE);
}
buf[sizeof(buf) - 1] = 0;
printf("Result = %s\n", buf);
close(data_socket);
exit(EXIT_SUCCESS);
}
运行客户端几次,服务器将read()
在某些时候阻止呼叫:
$ for i in {1..100}; do ./client 3 4 5 6; done
write END to socket, ret = 4
Result = 18
write END to socket, ret = 4
服务器输出:
$ ./server
Waiting to accept a connection...
Accepted socket fd = 4
Read data: 3
Read data: 4
Read data: 5
Read data: 6
Read data: END
Waiting to accept a connection...
Accepted socket fd = 4
Read data: 3
服务器阻塞while ((num_rd = read(cfd, buf, sizeof(buf))) > 0)
在线。
编辑:我的问题是为什么read()
块。AFAIKread()
将阻塞,直到从套接字读取至少1个字节的数据为止。在这种情况下,客户端发送的数据多于服务器读取的数据,因此我认为有可用的数据要从套接字读取。那么为什么read()
仍然会阻塞?
问题的核心是代码测试缓冲区中的第一条消息,而忽略了同一缓冲区可能包含多条消息,部分消息或任何其他组合的可能性(请参阅edit)。因此,END
有时会忽略该消息,并且read
循环永远不会终止。
该代码假定一个单一的 read
将获得哪些好处一个单一的 write
通话送了。
当客户端和服务器都在同一台计算机上时,这是非常不准确的,很少是真的,并且可能仅在某些情况下有效。
一个人read
可能write
同时读取2个电话,或者可能读取一半write
电话,然后再读取1.5个write
电话...
TCP / IP(与UDP不同)是一种流协议,并且不了解消息边界。
编辑:
为了弄清楚(按注释中的要求),假设有一个调用来read
收集以下数据"1234\0EN"
(下一个read
将收集"D\0"
)...该程序做什么?
另一种可能的情况是writes
一次性读取所有内容。即,buf
包含字符串"3\04\05\06\0END\0"
。
此时在循环内会发生什么?
在此示例场景中,if
语句(strncmp(buf, "END", sizeof(buf))
始终false
(且不安全),导致服务器永不中断while(read)
循环。
由于while
循环继续进行,read
因此在没有可用数据时,服务器将尝试另一个,导致服务器阻塞,直到客户端发送其他数据为止。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句