欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起探讨和分享Linux C/C++/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。
TCP通信机制
- 1. TCP三次握手
- 2. TCP四次挥手
- 3. TCP连接与数据传输过程
- 4. TCP滑动窗口机制
- 5. server服务端与client客户端编程实现
专栏:《Linux从小白到大神》《网络编程》
TCP是一种面向连接的安全的流式传输协议,TCP报文的格式如下
TCP在建立连接的时候需要进行三次握手(TCP握手时一定有SYN标志,不带SYN标志的为建立连接后的正常数据传输)
至此,三次握手成功,双向连接均已建立,可以开始数据传输了。示意图如下:
TCP断开连接时需要进行四次挥手:
四次挥手的过程如下:
首先看一个TCP连接与数据传输的示意图
在图中:
图中的
详细分析上述过程的完整图示如下
首先看滑动窗口的示意图
在图中,发送端速度快,接收端速度慢,一般来说谁先发送SYN谁就是客户端,因为客户端总是主动连接服务端,而服务端则被动等待客户端的连接。
在TCP中,滑动窗口实际上就是一块缓冲区(缓存)。在上图中,客户端与服务端进行数据传输的时候总是带有一个win 4096或win 6144等标志,这个win就代表滑动窗口的意思,而后面的数字则代表滑动窗口所表示的缓存区的大小。比如客户端发起的第一条握手请求,即fast sender所代表的1处,SYN, 0(0), win 4096,
通过server端发送的 win 6144,
此时,server端的缓冲区已经满了,不能再接收数据了,只有当server端把数据读出的时候,缓冲区才能空出位置接受新数据。当server端读取了2K数据,那么server端的缓冲区将空余出2K字节,请见上图中的序号10处,server端向client端回复 ACK,6145, win 2048,表示server端收到了6144字节(6KB)数据,6145是由6144+1来的,win 2048表示server端现在缓冲区空余2K空间。当server端再次读出2K数据的时候,server再次向client发送一条数据 ACK,6145, win 4096,这里ACK和确认序号不变,win所代表的窗口大小发生了变化,变成了当前空余的缓冲区大小4KB,可见上图中序号11所代表的数据传输,此时在server发送数据10和11之间,client端并没有发送数据,但是server端要向client端告知自己的空闲缓冲区大小。
最后,12-18的过程实际上就是四次挥手的过程,client发送FIN断开连接,此时server还没有处理完缓冲区中的数据,所以每处理一批数据都会向client发送一条信息并告知缓冲区剩余大小,直到缓冲区中的数据全部处理完毕,server向client发送FIN断开连接。实际上滑动窗口就是缓冲区的大小,并且在发送数据过程中,并不是client发一条server就必须收一条,也可以发多条收多条,这是因为TCP是流式传输,一端发送的数据虽然没有立即被处理,但是已经存起来了,就存在了滑动窗口所表示的缓冲区中,当缓冲区满了,发送端就会临时阻塞,等待接收端缓冲区出现剩余空间。
上面所描述的过程都是所有数据传输都成功的前提下进行的,实际上,每条数据的发送都可能会失败,当发生传输失败的情况,TCP会进行重传,重传的示意图如下:
server.c
/************************************************************>File Name : server.c>Author : Mindtechnist>Company : Mindtechnist>Create Time: 2022年08月14日 星期日 19时53分24秒
************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include int main(int argc, char* argv[])
{//创建用于监听的套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket err");exit(1);} //bindstruct sockaddr_in server_addr;//initmemset(&server_addr, 0, sizeof(server_addr)); //bzero(&server_addr, sizeof(serve_addr));server_addr.sin_family = AF_INET; //IPv4server_addr.sin_port = htons(8765);server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //使用本机任意IPint ret = bind(lfd, (struct sockaddr*)&server_addr, sizeof(server_addr));if(ret == -1){perror("bind err");exit(1);}//设置监听ret = listen(lfd, 128);if(ret == -1){perror("listen err");exit(1);}//等待并接受连接请求struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);int cfd = accept(lfd, (struct sockaddr*)&client_addr, &client_len);if(cfd == -1){perror("accept err");exit(1);}char ipbuf[64];printf("client ip: %s, port: %d\n",inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),ntohs(client_addr.sin_port));//通信while(1){//接收数据char buf[1024] = {0};int len = read(cfd, buf, sizeof(buf));if(len < 0){perror("read err");break;}else if(len == 0){printf("client disconnect ...\n");break;} else {//读到数据printf("read buf : %s\n", buf);for(int i = 0; i < len; i++){buf[i] = toupper(buf[i]);}printf("toupper read buf: %s\n", buf);//发送数据write(cfd, buf, strlen(buf) + 1);}}close(lfd);close(cfd);return 0;
}
client.c
/************************************************************>File Name : client.c>Author : Mindtechnist>Company : Mindtechnist>Create Time: 2022年08月15日 星期一 11时13分29秒************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include int main(int argc, char* argv[])
{if(argc < 2){printf("err:./exe port\n");return -1;}int port = atoi(argv[1]);//创建套接字int fd = socket(AF_INET, SOCK_STREAM, 0);if(fd == -1){perror("socket err");exit(1);}//连接服务器struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);int ret = connect(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));if(ret == -1){perror("connect err");exit(1);}//通信while(1){//接收键盘输入char buf[512];printf("please input string: \n");fgets(buf, sizeof(buf), stdin);//发送给服务器write(fd, buf, strlen(buf));//接收服务端数据int len = read(fd, buf, sizeof(buf));if(len < 0){perror("read err");exit(1);}else if(len == 0){printf("server close connect ...\n");break;}else{printf("read buf: %s, buflen: %d\n", buf, len);}} close(fd);return 0;
}
上一篇:样本与抽样分布(2)-基本分布