日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 网络编程详解四(流协议与粘包)

發(fā)布時(shí)間:2023/12/18 linux 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 网络编程详解四(流协议与粘包) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
TCP/IP協(xié)議是一種流協(xié)議,流協(xié)議是字節(jié)流,只有開始和結(jié)束,包與包之間沒有邊界,所以容易產(chǎn)生粘包,但是不會(huì)丟包。 UDP/IP協(xié)議是數(shù)據(jù)報(bào),有邊界,不存在粘包,但是可能丟包產(chǎn)生粘包問題的原因 1.SQ_SNDBUF套接字本身有緩沖區(qū)(發(fā)送緩沖區(qū),接收緩沖區(qū)) 2.tcp傳送的網(wǎng)絡(luò)數(shù)據(jù)最大值MSS大小限制 3.鏈路層也有MTU(最大傳輸單元)大小限制,如果數(shù)據(jù)包大于>MTU要在IP層進(jìn)行分片,導(dǎo)致消息分割。(可以簡單的認(rèn)為MTU是MSS加包頭數(shù)據(jù)) 4.tcp的流量控制和擁塞控制,也可能導(dǎo)致粘包 5.tacp延遲發(fā)送機(jī)制等等 結(jié)論:TCP/IP協(xié)議,在傳輸層沒有處理粘包問題,必須由程序員處理

粘包的解決方案--本質(zhì)上是要在應(yīng)用層維護(hù)消息與消息的邊界 1.定包長 2.包尾加\r\n(比如ftp協(xié)議) 3.包頭加上包體長度 4.更復(fù)雜的應(yīng)用層協(xié)議 粘包的幾種狀態(tài)

?

//粘包解決方案--包頭加上包體長度 //服務(wù)器 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>typedef struct _packet {int len; //定義包體長度char buf[1024]; //定義包體 } Packet;/** fd:文件描述符* buf:數(shù)據(jù)緩存區(qū)* count:讀取字符數(shù)* */ ssize_t readn(int fd, const void *buf, ssize_t count) {//定義臨時(shí)指針變量char *pbuf = (char *)buf;//定義每次已讀數(shù)據(jù)ssize_t nread = 0;//定義剩余數(shù)據(jù)ssize_t lread = count;while (lread > 0){nread = read(fd, pbuf, lread);/** 情況分析:假設(shè)b緩沖區(qū)buf足夠大* 如果nread==count,說明數(shù)據(jù)正好被讀完* nread<count,說明數(shù)據(jù)沒有被讀完,這種情況就是由于粘包產(chǎn)生的* socket只接收了數(shù)據(jù)的一部分,TCP/IP協(xié)議不可能出現(xiàn)丟包情況* nread==0,說明對(duì)方關(guān)閉文件描述符* nread==-1,說明read函數(shù)報(bào)錯(cuò)* nread>count,這種情況不可能存在* */if (nread == -1){//read()屬于可中斷睡眠函數(shù),需要做信號(hào)處理if (errno == EINTR)continue;perror("read() err");return -1;} else if (nread == 0){printf("client is closed !\n");//返回已經(jīng)讀取的字節(jié)數(shù)return count - lread;}//重新獲取 剩余的 需要讀取的 字節(jié)數(shù)lread = lread - nread;//指針后移pbuf = pbuf + nread;}return count; }/* fd:文件描述符* buf:數(shù)據(jù)緩存區(qū)* count:讀取字符數(shù)* */ ssize_t writen(int fd, const void *buf, ssize_t count) {//定義臨時(shí)指針變量char *pbuf = (char *)buf;//每次寫入字節(jié)數(shù)ssize_t nwrite = 0;//剩余未寫字節(jié)數(shù)ssize_t lwrite = count;while (lwrite > 0){nwrite = write(fd, pbuf, lwrite);if (nwrite == -1){if (errno == EINTR)continue;perror("write() err");return -1;} else if (nwrite == 0){printf("client is closed !\n");//對(duì)方關(guān)閉文件描述符,返回已經(jīng)寫完的字節(jié)數(shù)return count - lwrite;}lwrite -= nwrite;pbuf += nwrite;}return count; }int main(int arg, char *args[]) {//create socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1){perror("socket() err");return -1;}//reuseaddrint optval = 1;if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))== -1){perror("setsockopt() err");return -1;}//bindstruct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = inet_addr("127.0.0.1");if (bind(listenfd, (struct sockaddr *) &addr, sizeof(addr)) == -1){perror("bind() err");return -1;}//listenif(listen(listenfd,SOMAXCONN)==-1){perror("listen() err");return -1;}//acceptstruct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);int conn = accept(listenfd, (struct sockaddr *)&peeraddr,&peerlen);if (conn == -1){perror("accept() err");return -1;}Packet _packet;while (1){memset(&_packet, 0, sizeof(_packet));//獲取報(bào)文自定義包頭int rc = readn(conn, &_packet.len, 4);if (rc == -1){exit(0);} else if (rc < 4){exit(0);}//把網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)化成本地字節(jié)序int n = ntohl(_packet.len);//獲取報(bào)文自定義包體rc = readn(conn, _packet.buf, n);if (rc == -1){exit(0);} else if (rc < n){exit(0);}//打印報(bào)文數(shù)據(jù) fputs(_packet.buf, stdout);//將原來的報(bào)文數(shù)據(jù)發(fā)送回去printf("發(fā)送報(bào)文的長度%d\n", 4 + n);rc = writen(conn, &_packet, 4 + n);if (rc == -1){exit(0);} else if (rc < 4 + n){exit(0);}}return 0; } //粘包解決方案--包頭加上包體長度 //客戶端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>typedef struct _packet {int len; //定義包體長度char buf[1024]; //定義包體 } Packet;/** fd:文件描述符* buf:數(shù)據(jù)緩存區(qū)* count:讀取字符數(shù)* */ ssize_t readn(int fd, const void *buf, ssize_t count) {//定義臨時(shí)指針變量char *pbuf = (char *)buf;//定義每次已讀數(shù)據(jù)ssize_t nread = 0;//定義剩余數(shù)據(jù)ssize_t lread = count;while (lread > 0){nread = read(fd, pbuf, lread);/** 情況分析:假設(shè)b緩沖區(qū)buf足夠大* 如果nread==count,說明數(shù)據(jù)正好被讀完* nread<count,說明數(shù)據(jù)沒有被讀完,這種情況就是由于粘包產(chǎn)生的* socket只接收了數(shù)據(jù)的一部分,TCP/IP協(xié)議不可能出現(xiàn)丟包情況* nread==0,說明對(duì)方關(guān)閉文件描述符* nread==-1,說明read函數(shù)報(bào)錯(cuò)* nread>count,這種情況不可能存在* */if (nread == -1){//read()屬于可中斷睡眠函數(shù),需要做信號(hào)處理if (errno == EINTR)continue;perror("read() err");return -1;} else if (nread == 0){printf("client is closed !\n");//返回已經(jīng)讀取的字節(jié)數(shù)return count - lread;}//重新獲取 剩余的 需要讀取的 字節(jié)數(shù)lread = lread - nread;//指針后移pbuf = pbuf + nread;}return count; }/* fd:文件描述符* buf:數(shù)據(jù)緩存區(qū)* count:讀取字符數(shù)* */ ssize_t writen(int fd, const void *buf, ssize_t count) {//定義臨時(shí)指針變量char *pbuf = (char *)buf;//每次寫入字節(jié)數(shù)ssize_t nwrite = 0;//剩余未寫字節(jié)數(shù)ssize_t lwrite = count;while (lwrite > 0){nwrite = write(fd, pbuf, lwrite);if (nwrite == -1){if (errno == EINTR)continue;perror("write() err");return -1;} else if (nwrite == 0){printf("client is closed !\n");//對(duì)方關(guān)閉文件描述符,返回已經(jīng)寫完的字節(jié)數(shù)return count - lwrite;}lwrite -= nwrite;pbuf += nwrite;}return count; }int main(int arg, char *args[]) {//create socketint sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1){perror("socket() err");return -1;}//connectstruct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = inet_addr("127.0.0.1");if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1){perror("connect() err");return -1;}int rc = 0, numx = 0;Packet _packet;memset(&_packet, 0, sizeof(_packet));while (fgets(_packet.buf, sizeof(_packet.buf), stdin) != NULL){//發(fā)送數(shù)據(jù)numx = strlen(_packet.buf);//將本地字節(jié)轉(zhuǎn)化成網(wǎng)絡(luò)字節(jié)序_packet.len = htonl(numx);rc = writen(sockfd, &_packet, 4 + numx);if (rc == -1){return -1;} else if (rc < 4 + numx){return -1;}//接收數(shù)據(jù)memset(&_packet, 0, sizeof(_packet));//獲取包頭rc = readn(sockfd, &_packet.len, 4);if (rc == -1){return -1;} else if (rc < 4){return -1;}//將網(wǎng)絡(luò)字節(jié)轉(zhuǎn)化成本地字節(jié)numx = ntohl(_packet.len);//printf("接收數(shù)據(jù)的大小是%d\n",numx);//獲取包體rc = readn(sockfd, &_packet.buf, numx);if (rc == -1){return -1;} else if (rc < numx){return -1;}//打印包體 fputs(_packet.buf,stdout);memset(&_packet, 0, sizeof(_packet));}return 0; }

?

轉(zhuǎn)載于:https://www.cnblogs.com/zhanggaofeng/p/6134757.html

總結(jié)

以上是生活随笔為你收集整理的Linux 网络编程详解四(流协议与粘包)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。