4.3 实现迭代服务器端 客户端
2025/10/5大约 4 分钟
4.3 实现迭代服务器端 客户端
实现回声服务器端:
服务端将客户端传输的字符串数据原封不动地传回客户端
迭代服务器
顺序调用listen-》accept-》read/write-》close(由accept创建的,用于和client进行通讯的socket),只能处理一个客户的一次请求。服务器应该可以服务于多个客户,并且在建立连接后保持连接状态,持续进行数据交换。
通过循环,不断的从等待连接队列中获取待连接客户端,创建连接,然后响应,这就是迭代服务器。
调用顺序:
实现
//...
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len = 0;
if(argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1)
unix_error("serv_socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if(bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
unix_error("bind() error ");
if(listen(sock, 5) == -1)
unix_error("listen() error ");
struct sockaddr_in client_addr;
socklen_t client_addrlen;
while(1)
{
int clientSock = accept(sock, (struct sockaddr*)&client_addr, &client_addrlen);
if(clientSock == -1)
unix_error("accept() error");
printf("Connected client:%s , %d \n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
while((str_len = read(clientSock, message, BUFSIZ)) !=0 )
write(clientSock, message, str_len);
close(clientSock);
}
close(sock);
return 0;
}效果:
运行服务端:
./Network 192.168.56.101 10087网络调试工具连接服务端:
Server connected from local 192.168.56.1 :27952client 地址:192.168.56.1,port:27952
服务端打印:
Connected client:192.168.56.1 , 27952客户端发送以及接收:
[2025-10-04 20:19:05.145]# SEND ASCII/26 >>>
hello server, I am client1
[2025-10-04 20:19:05.156]# RECV ASCII/26 from SERVER <<<
hello server, I am client1发送以及接收内容相同
断开当前连接,创建一个新的连接,发送数据,接收到发送的数据。
回声功能似乎没有问题,但是。。。
发现一个问题
在client0 已经连接到server的情况下,client1仍然可以连接(工具提示连接成功)。然是client1发送消息不会有回复。如果断开client0的连接,那么client1立即收到之前发送的消息的回复。
如果client1发送了多条消息,那么可能被拼接到一条回复里。
[2025-10-04 21:13:02.135]# SEND ASCII/6 >>>
nihaoa
[2025-10-04 21:13:02.281]# SEND ASCII/6 >>>
nihaoa
[2025-10-04 21:13:02.421]# SEND ASCII/6 >>>
nihaoa
[2025-10-04 21:13:04.480]# RECV ASCII/48 from SERVER <<<
nihaoanihaoanihaoanihaoanihaoanihaoanihaoanihaoa客户端
实现:
if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
unix_error("connect() error");
else
puts("Connected ... \n");
while(1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if(!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE - 1);
message[str_len] = 0;
printf("Message from server: %s\n", message);
}
close(sock);| 行号 | 功能 | 说明 |
|---|---|---|
| 1 | 连接到服务器 | |
| 8 | 输入提示 | |
| 9 | 获取输入 | |
| 10 | 判断是否退出 | 通过输入q退出程序 |
| 12 | 发送输入的消息 | 输入的类型为字符串,字符串长度不包含结尾符号 |
| 13 | 读取接收的消息 | |
| 14 | 补上字符串结束符 | |
| 15 | 打印接收消息 |
效果:
启动服务端
启动客户端输入消息
Connected ...
Input message(Q to quit): abc
Message from server: abc
Input message(Q to quit): efg
Message from server: efg
Input message(Q to quit): hij
Message from server: hij存在的问题
回声要求每次发声(客户端向服务器发消息)都有回声(服务端响应)且回声 和 发声 相同。由于TCP不存在数据边界-多次发送一次接收 或 一次发送多次接收,造成发声和回声不同。
- 发送的数据过大,可能被拆分为多个包
- 客户端可能在未接收到全部数据时就进行读取
服务端虽然也不知道数据边界,但是服务端根本不需要知道,它只要把接收到的再发送回去就可以了。