基于TCP传输的粘包问题
来源:互联网 发布:js获取选择框的值 编辑:程序博客网 时间:2024/05/21 06:33
1我们都知道TCP传输,是基于字节流传输的,所以流与流直接传输就会产生边界问题,我个人对粘包的理解就是,TCP传输无法获悉不同包与包之间的“界限”。
如果对等接受方彼此直接没有约定好传输数据大小的话,就会出现解析数据不准确问题,而且传输数据小于约定大小空间的话,也会出现浪费空间问题,为了解决这种问题,通常才有包头+包体传输,这样对等方就可以分辨出不同的包,所对应的数据。(该办法解决的是发送不定长包)
2.UDP是基于数据包协议,所以也就不存在粘包问题,虽然UDP是不可靠的传输协议,可能存在丢包,失序,错序,重复等问题,但是他的效率是比UDP高的多,我们知道TCP可靠性方面有一个超时重连机制,要让UDP实现可靠传输的话,就需要模拟一套TCP传输机制。
粘包问题解决方法代码如下:回射服务器代码,数据包格式:包头+包体
服务端代码:
/*************************************************************************> File Name: ser.cpp> Created Time: Sun 29 Oct 2017 09:00:58 PM CST ************************************************************************/#include <iostream>using namespace std;#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#include <stdio.h>#include <string.h>//处理粘包问题//封装read函数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) { cout << " < 0" << endl; if(errno == EINTR) //信号打断 continue; return -1; } else if(nread == 0) { return count-nleft; } bufp += nread;//偏移到屁股 nleft -= nread; } return count;}//封装write方法ssize_t writen(int fd, const void* buf, size_t count){ size_t nleft = count; ssize_t nwritten; char* bufp = (char*)buf; while(nleft > 0) { if((nwritten = write(fd, bufp, nleft)) < 0) { if(errno == EINTR) //信号中断 continue; return -1; } else if(nwritten == 0) { continue; //return count-nleft; } bufp += nwritten; nleft -= nwritten; } return count;}/////////////////////////////////////void do_server(int conn){ struct packet recvbuf; while(1) { memset(recvbuf.buf, 0, sizeof(recvbuf.buf)); int ret = readn(conn, &recvbuf.len, 4); if(ret == -1) { perror("read"); exit(1); } else if(ret < 4) { cout << "clien close\n"; break; } int m = recvbuf.len; cout << "n:" << m << endl; int n = ntohl(recvbuf.len); cout << "n:" << n << endl; ret = readn(conn, &recvbuf.buf, n); if(ret == -1) { perror("read"); exit(1); } if(ret < n) { cout << "clien close\n"; break; } fputs(recvbuf.buf, stdout); writen(conn, &recvbuf, 4+n); } close(conn);}int main(){ int listenfd; //初始化监听套接字(默认是主动套接字,发起连接) if((listenfd = socket(AF_INET, SOCK_STREAM, 0))<0) { perror("socket created failed"); exit(1); } //bind函数,bind一个本地地址到套接字 struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8881); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1")*/////////////////////////////getsockopt/////////// int on = 1;//开启地址重复利用 if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { perror("setsockopt"); }////////////////////////////////////////////////// if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("bind a local addr to listenfd failed..."); exit(1); } //listen 讲套接字用于监听进入的连接,讲套接字从close状态转换成监听状态 //也就是说调用listen函数就将套接字由主动变为被动套接字(接受连接) if(listen(listenfd, SOMAXCONN) < 0)//SOMAXCONN 默认最大队列 { perror("listenfd to listen client connect failed.."); exit(1); } //accept 从已经连接的队列中返回是第一个连接,队列为空就阻塞 //成功返回一个新的套接字,也就是连接套接字,并且这个新套接字是主动套接字 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) { perror("accept client connect failed..."); exit(1); } cout << "Ip:" << inet_ntoa(peeraddr.sin_addr) << " port:" << ntohs(peeraddr.sin_port) << endl; pid = fork(); if(pid < 0) { perror("fork"); exit(1); } if(pid == 0) // 子进程处理客户端连接 { close(listenfd); do_server(conn); } } return 0;}回射服务器,客户端代码如下
/*************************************************************************> File Name: ser.cpp> Created Time: Sun 29 Oct 2017 09:00:58 PM CST ************************************************************************/#include <iostream>using namespace std;#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#include <stdio.h>#include <string.h>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(nread == 0) { return count-nleft; } bufp += nread; nleft -= nread; } return count;}ssize_t writen(int fd, const void* buf, size_t count){ size_t nleft = count; ssize_t nwritten; char* bufp = (char*)buf; while(nleft > 0) { if((nwritten = write(fd, bufp, nleft)) < 0) { if(errno == EINTR) continue; return -1; } else if(nwritten == 0) { continue; //return count-nleft; } bufp += nwritten; nleft -= nwritten; } return count;}/////////////////////////////////////int main(){ int sock; //初始化监听套接字(默认是主动套接字,发起连接) if((sock = socket(AF_INET, SOCK_STREAM, 0))<0) { perror("socket created failed"); exit(1); } struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8881); //servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //connect ,发起连接 if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("connect ser is failed\n"); exit(1); } struct packet sendbuf; struct packet recvbuf; memset(&sendbuf, 0, sizeof(sendbuf)); memset(&recvbuf, 0, sizeof(recvbuf)); int n; while(fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin) != NULL) { n = strlen(sendbuf.buf); sendbuf.len = htonl(n); //网络字节序 writen(sock, &sendbuf, 4+n);//sizeof()发送定长包 int ret = readn(sock, &recvbuf.len, 4); if(ret == -1) { perror("read"); exit(1); } else if(ret < 4) { cout << "clien close\n"; break; } n = ntohl(recvbuf.len); ret = readn(sock, &recvbuf.buf, n); if(ret == -1) { perror("read"); exit(1); } if(ret < n) { cout << "clien close\n"; break; } fputs(recvbuf.buf,stdout); memset(recvbuf.buf, 0, sizeof(recvbuf.buf)); memset(sendbuf.buf, 0 ,sizeof(sendbuf.buf)); } close(sock); return 0;}
阅读全文
0 0
- 基于TCP传输的粘包问题
- TCp传输粘包问题
- TCP传输粘包问题
- 关于TCP传输的沾包问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输"粘包"问题
- 解决TCP网络传输“粘包”问题
- 解决TCP网络传输“粘包”问题
- 欢迎使用CSDN-markdown编辑器
- ubuntu下安装opencv 2.4.9
- spring aop的实现原理
- 二叉树的建立与遍历【数据结构实验报告】
- java中的构造方法
- 基于TCP传输的粘包问题
- spring boot框架学习6-spring boot的web开发(2)
- SpringBoot34-springboot应用监控-http,JMX,SSH
- 判断字符串是否为对称--双链表
- 慕课网:Linux 达人养成计划<1> 系统分区
- TensorFlow创建变量
- 2017.11.6 开始我的博客生涯
- Python高级编程-如何判断字符串a是否是以字符串b开头或结尾?
- 「设计模式」JavaScript