1.流协议与粘包

TCP是一个基于字节流的传输服务,"流"意味着TCP所传输的数据是没有边界的。这不同于UDP提供基于消息的传输服务,其传输的数据是有边界的。TCP的发送方无法保证对等方每次接收到的是一个完整的数据包。主机A向主机B发送两个数据包,主机B的接收情况可能是

2.粘包产生的原因

(1)应用层调用write方法,将应用层的缓冲区中的数据拷贝到套接字的发送缓冲区。而发送缓冲区有一个SO_SNDBUF的限制,如果应用层的缓冲区数据大小大于套接字发送缓冲区的大小,则数据需要进行多次的发送。

(2)TCP所传输的报文段有MSS的限制,如果套接字缓冲区的大小大于MSS,也会导致消息的分割发送。

(3)由于链路层最大发送单元MTU,在IP层会进行数据的分片。

3.粘包处理方案

(1)发送定长包。如果每个消息的大小都是一样的,那么在接收对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息。

(2)包尾加上\r\n标记。FTP协议正是这么做的。但问题在于如果数据正文中也含有\r\n,则会误判为消息的边界。

(3)包头加上包体长度。包头是定长的4个字节,说明了包体的长度。接收对等方先接收包体长度,依据包体长度来接收包体。

(4)使用更加复杂的应用层协议。

4.回射客户/服务器

echoserver.cpp

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \}while(0)/*param1:fd, param2:buf, param3:count
return:读取成功字节数
ssize_t:有符号
size_t:无符号*/struct packet {int len;char buf[1024];
};ssize_t readn(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nread;  //已经读了char* bufp = (char*)buf;while(nleft > 0) {if((nread = read(fd, bufp, nleft)) < 0) {if(errno == EINTR) continue;return -1;} else if (0 == nread) {return count - nleft;}bufp += nread;nleft -= nread;}return count;
}/*
param1:fd, param2:buf, param3:count
return:已经发送了多少
*/
ssize_t writen(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nwrite;char* bufp = (char*)buf;while(nleft > 0) {if((nwrite = write(fd, bufp, nleft)) < 0) { if(errno == EINTR)continue;return -1;} else if(0 == nwrite) {continue;}bufp += nwrite;nleft -= nwrite;}return count;
}void do_service(int conn) {//char recvbuf[1024];struct packet recvbuf;int n;while(1) {memset(&recvbuf, 0, sizeof(recvbuf));int ret = readn(conn, &recvbuf.len, 4);if(-1 == ret) {ERR_EXIT("read");} else if(4 > ret) {printf("client close\n");break;}n = ntohl(recvbuf.len);ret = readn(conn, recvbuf.buf, n);if(ret < n) {  //server端知道client关闭printf("client close\n");break;} else if(-1 == ret) {ERR_EXIT("read");}fputs(recvbuf.buf, stdout);writen(conn, &recvbuf, 4+n);}
}int main () {int listenfd;if(( listenfd= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)//if((listenfd= socket(PF_INET, SOCK_STREAM, 0)) <0) ERR_EXIT("socket");struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(5188);//servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//inet_aton("127.0.0.1", &servaddr.sin_addr);int on = 1;if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)ERR_EXIT("setsockopt");if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))<0)ERR_EXIT("bind");if(listen(listenfd, SOMAXCONN) < 0)ERR_EXIT("listen");struct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);int conn;pid_t pid;while(1) {if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)ERR_EXIT("accept");printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));pid = fork();if(-1 == pid) {ERR_EXIT("fork");}if(pid == 0) {  //child close(listenfd);do_service(conn);exit(EXIT_SUCCESS);} else {    //parentclose(conn);}}close(listenfd);close(conn);return 0;
}

echoclient.cpp

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \}while(0)struct packet {int len;char buf[1024];
};ssize_t readn(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nread;  //已经读了char* bufp = (char*)buf;while(nleft > 0) {if((nread = read(fd, bufp, nleft)) < 0) {if(errno == EINTR) continue;return -1;} else if (0 == nread) {return count - nleft;}bufp += nread;nleft -= nread;}return count;
}ssize_t writen(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nwrite;char* bufp = (char*)buf;while(nleft > 0) {if((nwrite = write(fd, bufp, nleft)) < 0) {    if(errno == EINTR)continue;return -1;} else if(0 == nwrite) {continue;}bufp += nwrite;nleft -= nwrite;}return count;
}int main () {int sock;if(( sock= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)ERR_EXIT("socket");sock = socket(PF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(5188);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)ERR_EXIT("sockect");struct packet sendbuf;struct packet recvbuf;memset(&sendbuf, 0, sizeof(sendbuf));memset(&recvbuf, 0, sizeof(recvbuf));//char sendbuf[1024] = {0};   //char recvbuf[1024] = {0};int n;while(fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin) != NULL) {n = strlen(sendbuf.buf);sendbuf.len = htonl(n);  //注意主机字节序-->网络字节序 writen(sock, &sendbuf, 4+n);  //定制协议//writen(sock, sendbuf, sizeof(sendbuf));  //发送定长包//writen(sock, sendbuf, strlen(sendbuf));int ret = readn(sock, &recvbuf.len, 4);if(-1 == ret) {ERR_EXIT("read");} else if(4 > ret) {printf("client close\n");break;}n = ntohl(recvbuf.len);ret = readn(sock, recvbuf.buf, n);if(ret < n) {  //server端知道client关闭printf("client close\n");break;} else if(-1 == ret) {ERR_EXIT("read");}//readn(sock, recvbuf, sizeof(recvbuf));fputs(recvbuf.buf, stdout);memset(&sendbuf, 0, sizeof(sendbuf));memset(&recvbuf, 0, sizeof(recvbuf));}close(sock);return 0;
}

socket编程(四)相关推荐

  1. (P9)socket编程四:流协议与粘(nian)包,粘包产生的原因,粘包处理方案,readn,writen 6.回射客户/服务器

    文章目录 1.流协议与粘(nian)包 2.粘包产生的原因 4.粘包处理方案 5.readn,writen 6.回射客户/服务器 1.流协议与粘(nian)包 tcp是基于字节流的传输服务(字节流是无 ...

  2. C语言 socket编程实例

    C语言 socket编程实例 一. 面向连接的流式套接字 C/S 例子 二. 非阻塞的多人聊天服务器端例子 三. 简单的 IPv6 UDP socket编程 四.使用wireshark抓包分析tcp协 ...

  3. Java从零开始学四十五(Socket编程基础)

    一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...

  4. socket recv 服务端阻塞 python_网络编程(基于socket编程)

    网络编程(基于socket编程) socket套接字:应用程序通常通过socket"套接字"向网络发送请求或应答网络请求,是主机间或同一计算机中的进程间相互通讯 socket是介于 ...

  5. 基于TCP(面向连接)的Socket编程

    基于TCP(面向连接)的Socket编程 一.客户端: 1.打开一个套接字(Socket); 2.发起连接请求(connect); 3.如果连接成功,则进行数据交换(read.write.send.r ...

  6. IP地址的三种表示格式及在Socket编程中的应用

    转自:http://blog.csdn.net/hguisu/article/details/7449955 使用TCP/IP协议进行网络应用开发的朋友首先要面对的就是对IP地址信息的处理.IP地址其 ...

  7. python基础之socket编程

    阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现象 ...

  8. 转Go语言TCP Socket编程

    授权转载: Tony Bai 原文连接: https://tonybai.com/2015/11/17/tcp-programming-in-golang/ Golang的主要 设计目标之一就是面向大 ...

  9. 最基本的Socket编程 C#版

    说明:此示例在.net2005\xp下运行通过 示例程序是同步套接字程序,功能很简单,只是客户端发给服务器一条信息,服务器向客户端返回一条信息:这里只是一个简单的示例,是一个最基本的socket编程流 ...

  10. golang TCP Socket编程

    Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分.在日常应用中,我们也可以看到Go中的net以及其subdirectories下的包均 ...

最新文章

  1. 抽象工厂与工厂模式例子
  2. 【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | LoadedApk 源码分析 )
  3. CentOS7+CDH5.14.0安装全流程记录,图文详解全程实测-7主节点CM安装子节点Agent配置...
  4. android ontoch事件无反应_一切从android的handler说起(三)
  5. 海康威视Web端视频开发
  6. 为什么我们会看到 SAP Spartacus 服务器端渲染 `rendering in process` 的日志
  7. php 打开pdf文件附件,pdf里怎么链接到附件
  8. 【技术解决方案】RTP_UDP传输过程中数据丢失的解决方案
  9. pycharm远程连接服务器(docker)调试+ssh连接多次报错
  10. python如何计算整数和_python 整数和浮点数
  11. Python测试开发django4.templates模板配置
  12. 通过 Azure 媒体管理门户开始使用直播流媒体
  13. opencv的第一个lena图片显示
  14. python编写beta计算器_|python编写计算器
  15. 脚上有一个很灵的止咳穴位
  16. 微信小程序接口文档PHP,微信小程序API 导航
  17. 三代基因组测序技术原理简介
  18. 【秋招】秋招最全指南,如何准备,如何投递,以及面试攻略大全分享!
  19. Android系统安全机制
  20. 挑战感知极限:智能安全感知驱动设计

热门文章

  1. mysql之delete删除记录后数据库大小不变
  2. 利用stack结构,将中缀表达式转换为后缀表达式并求值的算法实现
  3. 问题-Delphi 中使用TStringList后,报out of memory 的解决方法
  4. (转)asp.net夜话之十一:web.config详解
  5. 我所理解的 C++ 反射机制
  6. 解决MacOS升级后出现xcrun: error: invalid active developer path, missing xcrun的问题
  7. fetch 另一种ajax解决方案
  8. WCF并发连接数的问题
  9. 【C/C++】拷贝构造函数 赋值运算符的重载
  10. 安装linux环境及相关包方法