基于tcp的服务器端/客户端
详细的内容看这里,我就不复制了:
https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch04/README.md
TCP/IP 协议栈共分为 4 层,可以理解为数据收发分成了 4 个层次化过程,通过层次化的方式来解决问题
- 链路层: 物理层,负责网络设备之间的通信,如网卡、网线等
- IP层: 数据包进行中线选择
- TCP/UDP层: 负责数据包的传输,保证数据包的可靠性和顺序性
实现基于 TCP 的服务器/客户端
代码:
hello_server
1{{#include ch04/hello_server.c}}
代码: hello_client
1{{#include ch04/hello_client.c}}
echo服务器/客户端
程序运行的基本方式:
- 服务器端在同一时刻只与一个客户端相连,并提供回声服务。
- 服务器端依次向 5 个客户端提供服务并退出。
for循环5次 - 客户端接受用户输入的字符串并发送到服务器端。
- 服务器端将接受的字符串数据传回客户端,即「回声」
- 服务器端与客户端之间的字符串回声一直执行到客户端输入 Q 为止。
1{{#include ch04/echo_server.c}}
1{{#include ch04/echo_client.c}}
FAQ
问: 我在在linux下使用socket进行tcp网络编程,如果client端调用了close(sock)函数,那么server端口的read函数会读取到什么?
答:
在Linux下使用socket进行TCP网络编程时,如果客户端调用了close(sock)函数,服务器端的read函数的行为取决于客户端关闭连接的方式和时机。
通常情况下,当客户端调用close(sock)函数时,会向服务器发送一个FIN包,表示客户端想要关闭连接。服务器收到FIN包后,会做出以下响应:
如果服务器已经读取了所有客户端发送的数据,那么
read函数会返回0,表示已经到达文件末尾(EOF)。这意味着客户端已经关闭了连接,并且没有更多的数据可读。如果服务器还没有读取完客户端发送的所有数据,那么
read函数会继续读取剩余的数据,直到读取完所有数据或者遇到错误为止。在这种情况下,read函数会返回实际读取到的字节数。当所有数据读取完毕后,再次调用read函数会返回0,表示已经到达文件末尾(EOF)。如果客户端在发送数据的过程中突然关闭了连接,那么服务器的
read函数可能会返回一个错误,通常是ECONNRESET错误,表示连接被重置。
需要注意的是,如果客户端在关闭连接之前没有发送所有的数据,那么服务器可能会丢失一部分数据。为了确保数据的完整性,通常建议在关闭连接之前,先调用shutdown(sock, SHUT_WR)函数,告诉操作系统不再发送数据,然后再调用close(sock)函数关闭连接。
总之,服务器端的read函数在客户端关闭连接时的行为取决于具体的情况:
- 如果客户端发送了所有数据并关闭连接,
read函数会返回0,表示EOF。 - 如果客户端关闭连接时还有未读取的数据,
read函数会继续读取剩余数据,直到读取完毕,然后返回0,表示EOF。 - 如果客户端异常关闭连接,
read函数可能会返回错误,如ECONNRESET。
作为服务器端,需要正确处理这些不同的情况,以确保程序的正确性和鲁棒性。
问: 如何检测到 read函数返回一个错误, 例如是ECONNRESET?
在Linux系统中,当read函数返回错误时,会设置全局变量errno来指示具体的错误类型。你可以通过检查errno的值来判断是否发生了ECONNRESET错误。
下面是一个示例代码,演示了如何检测read函数返回的错误:
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <errno.h>
5#include <string.h>
6
7ssize_t read_data(int sockfd, void *buf, size_t len) {
8 ssize_t num_read = read(sockfd, buf, len);
9 if (num_read == -1) {
10 if (errno == ECONNRESET) {
11 printf("Connection reset by peer\n");
12 } else {
13 perror("read");
14 }
15 return -1;
16 }
17 return num_read;
18}
在上面的代码中,read_data函数封装了read函数的调用。它检查read函数的返回值:
- 如果返回值为-1,表示发生了错误。此时,通过检查
errno的值来判断具体的错误类型:- 如果
errno的值等于ECONNRESET,表示连接被对端重置,可以输出相应的错误信息。 - 否则,可以使用
perror函数输出错误信息,perror函数会根据errno的值自动生成对应的错误描述。
- 如果
- 如果返回值不为-1,表示读取成功,函数返回实际读取到的字节数。
在实际使用时,你可以根据需要对read_data函数进行调用,并根据返回值进行相应的处理:
1char buffer[1024];
2ssize_t num_read = read_data(sockfd, buffer, sizeof(buffer));
3if (num_read == -1) {
4 // 处理错误情况
5} else if (num_read == 0) {
6 // 处理连接关闭的情况
7} else {
8 // 处理成功读取数据的情况
9}
通过检查read_data函数的返回值,你可以判断是否发生了错误,并根据具体的错误类型采取相应的处理措施。
总之,通过检查read函数的返回值和errno的值,你可以检测并处理read函数返回的错误,包括ECONNRESET错误。这样可以增强程序的健壮性和可靠性。