网络编程Socket之TCP之read/write

本文深入探讨了TCP套接字的工作原理,包括write调用的行为、数据确认过程、缓冲区管理以及UDP与TCP的区别。通过客户端和服务端的代码示例,帮助理解套接字通信的关键细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从写一个TCP套接字的write调用成功返回仅仅表示我们可以重新使用原来的应用进程缓冲区,并不代表对端TCP或应用进程已接收到数据。


对端TCP必须确认收到的数据,伴随来自对端的ACK的不断到达,本端TCP至此才能从套接字发送缓冲区中丢弃已确认的数据,TCP必须为已发送的数据保留一个副本,直到它被对端确认为止。


UDP不保存应用进程数据的副本因此无需一个真正的发送缓冲区,write调用成功返回表示所写的数据报或其所有分片已被加入数据链路层的输出队列。


对于read调用(套接字标志为阻塞),如果接收缓冲区中有20字节,请求读100个字节,就会返回20。对于write调用(套接字标志为阻塞),如果请求写100个字节,而发送缓冲区中只有20个字节的空闲位置,那么write会阻塞,直到把100个字节全部交给发送缓冲区才返回,如果write中得套接字标志为非阻塞,则直接返回20,因此我们可以实现自己的readn和writen函数。

 

每个TCP套接字都有一个发送缓冲区和一个接收缓冲区,每个UDP套接字都有一个接收缓冲区;


先模拟服务端阻塞:

client

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct sockaddr_in serverAdd;  
  2.   
  3. bzero(&serverAdd, sizeof(serverAdd));  
  4. serverAdd.sin_family = AF_INET;  
  5. serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);  
  6. serverAdd.sin_port = htons(SERV_PORT);  
  7.   
  8. connfd = socket(AF_INET, SOCK_STREAM, 0);  
  9.   
  10. int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));  
  11. if (connResult < 0) {  
  12.     printf("连接失败,errno = %d\n",errno);  
  13.     close(connfd);  
  14.     return ;  
  15. }  
  16. else  
  17. {  
  18.     printf("连接成功\n");  
  19. }  
  20.   
  21. ssize_t writeLen;  
  22.   
  23. char sendMsg[246988] = {0};  
  24.   
  25. int count = 0;  
  26. while (1)  
  27. {  
  28.     count++;  
  29.     if (count == 5) {  
  30.         exit(0);  
  31.     }  
  32.     writeLen = write(connfd, sendMsg, sizeof(sendMsg));  
  33.     if (writeLen < 0) {  
  34.         printf("发送失败\n");  
  35.         close(connfd);  
  36.         return ;  
  37.     }  
  38.     else  
  39.     {  
  40.         printf("发送成功\n");  
  41.     }  
  42.       
  43. }  


server:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int main(int argc, const char * argv[])  
  2. {  
  3.   
  4.     struct sockaddr_in serverAdd;  
  5.     struct sockaddr_in clientAdd;  
  6.       
  7.     bzero(&serverAdd, sizeof(serverAdd));  
  8.     serverAdd.sin_family = AF_INET;  
  9.     serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);  
  10.     serverAdd.sin_port = htons(SERV_PORT);  
  11.       
  12.     socklen_t clientAddrLen;  
  13.       
  14.     int listenfd = socket(AF_INET, SOCK_STREAM, 0);  
  15.     int yes = 1;  
  16.     setsockopt(listenfd,  
  17.                SOL_SOCKET, SO_REUSEADDR,  
  18.                (void *)&yes, sizeof(yes));  
  19.       
  20.     if (listenfd < 0) {  
  21.         printf("创建socket失败\n");  
  22.         close(listenfd);  
  23.         return -1;  
  24.     }  
  25.       
  26.     int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));  
  27.     if (bindResult < 0) {  
  28.         close(listenfd);  
  29.         printf("绑定端口失败,errno = %d\n",errno);  
  30.         return -1;  
  31.     }  
  32.     else  
  33.     {  
  34.         printf("绑定端口成功\n");  
  35.     }  
  36.       
  37.     listen(listenfd, 20);  
  38.       
  39.     int connfd;  
  40.     unsigned char recvMsg[246988];  
  41.       
  42.       
  43.     clientAddrLen = sizeof(clientAdd);  
  44.     connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);  
  45.     if (connfd < 0) {  
  46.         close(listenfd);  
  47.         printf("连接失败\n");  
  48.         return -1;  
  49.     }  
  50.     else  
  51.     {  
  52.         printf("连接成功\n");  
  53.         int rcvbuf_len;  
  54.         socklen_t len = sizeof(rcvbuf_len);  
  55.           
  56.         if( getsockopt( connfd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf_len, &len ) < 0 ){  
  57.             perror("getsockopt: ");  
  58.         }  
  59.         printf("the recv buf len: %d\n", rcvbuf_len );  
  60.           
  61.     }  
  62.       
  63.     ssize_t totalLen = 0;  
  64.     while (1)  
  65.     {  
  66.         sleep(1);  
  67.         ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));  
  68.         printf("readLen:%ld\n",readLen);  
  69.         if (readLen < 0) {  
  70.             printf("读取失败\n");  
  71.             return -1;  
  72.         }  
  73.         else if (readLen == 0) {  
  74.             printf("读取完成-len = %ld\n",totalLen);  
  75.             close(listenfd);  
  76.             return 0;  
  77.         }  
  78.         else  
  79.         {  
  80.             totalLen += readLen;  
  81.         }  
  82.           
  83.     }  
  84.          
  85.     close(connfd);  
  86.   
  87.     return 0;  
  88. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值