Linux 網絡編程詳解四(流協議與粘包)

TCP/IP協議是一種流協議,流協議是字節流,只有開始和結束,包與包之間沒有邊界,所以容易產生粘包,但是不會丟包。
UDP/IP協議是數據報,有邊界,不存在粘包,但是可能丟包
產生粘包問題的原因
1.SQ_SNDBUF套接字本身有緩沖區(發送緩沖區,接收緩沖區)
2.tcp傳送的網絡數據最大值MSS大小限制
3.鏈路層也有MTU(最大傳輸單元)大小限制,如果數據包大于>MTU要在IP層進行分片,導致消息分割。(可以簡單的認為MTU是MSS加包頭數據)
4.tcp的流量控制和擁塞控制,也可能導致粘包
5.tacp延遲發送機制等等
結論:TCP/IP協議,在傳輸層沒有處理粘包問題,必須由程序員處理

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

?

//粘包解決方案--包頭加上包體長度
//服務器
#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:數據緩存區* count:讀取字符數* */
ssize_t readn(int fd, const void *buf, ssize_t count)
{//定義臨時指針變量char *pbuf = (char *)buf;//定義每次已讀數據ssize_t nread = 0;//定義剩余數據ssize_t lread = count;while (lread > 0){nread = read(fd, pbuf, lread);/** 情況分析:假設b緩沖區buf足夠大* 如果nread==count,說明數據正好被讀完* nread<count,說明數據沒有被讀完,這種情況就是由于粘包產生的* socket只接收了數據的一部分,TCP/IP協議不可能出現丟包情況* nread==0,說明對方關閉文件描述符* nread==-1,說明read函數報錯* nread>count,這種情況不可能存在* */if (nread == -1){//read()屬于可中斷睡眠函數,需要做信號處理if (errno == EINTR)continue;perror("read() err");return -1;} else if (nread == 0){printf("client is closed !\n");//返回已經讀取的字節數return count - lread;}//重新獲取 剩余的 需要讀取的 字節數lread = lread - nread;//指針后移pbuf = pbuf + nread;}return count;
}/* fd:文件描述符* buf:數據緩存區* count:讀取字符數* */
ssize_t writen(int fd, const void *buf, ssize_t count)
{//定義臨時指針變量char *pbuf = (char *)buf;//每次寫入字節數ssize_t nwrite = 0;//剩余未寫字節數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");//對方關閉文件描述符,返回已經寫完的字節數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));//獲取報文自定義包頭int rc = readn(conn, &_packet.len, 4);if (rc == -1){exit(0);} else if (rc < 4){exit(0);}//把網絡字節序轉化成本地字節序int n = ntohl(_packet.len);//獲取報文自定義包體rc = readn(conn, _packet.buf, n);if (rc == -1){exit(0);} else if (rc < n){exit(0);}//打印報文數據
        fputs(_packet.buf, stdout);//將原來的報文數據發送回去printf("發送報文的長度%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:數據緩存區* count:讀取字符數* */
ssize_t readn(int fd, const void *buf, ssize_t count)
{//定義臨時指針變量char *pbuf = (char *)buf;//定義每次已讀數據ssize_t nread = 0;//定義剩余數據ssize_t lread = count;while (lread > 0){nread = read(fd, pbuf, lread);/** 情況分析:假設b緩沖區buf足夠大* 如果nread==count,說明數據正好被讀完* nread<count,說明數據沒有被讀完,這種情況就是由于粘包產生的* socket只接收了數據的一部分,TCP/IP協議不可能出現丟包情況* nread==0,說明對方關閉文件描述符* nread==-1,說明read函數報錯* nread>count,這種情況不可能存在* */if (nread == -1){//read()屬于可中斷睡眠函數,需要做信號處理if (errno == EINTR)continue;perror("read() err");return -1;} else if (nread == 0){printf("client is closed !\n");//返回已經讀取的字節數return count - lread;}//重新獲取 剩余的 需要讀取的 字節數lread = lread - nread;//指針后移pbuf = pbuf + nread;}return count;
}/* fd:文件描述符* buf:數據緩存區* count:讀取字符數* */
ssize_t writen(int fd, const void *buf, ssize_t count)
{//定義臨時指針變量char *pbuf = (char *)buf;//每次寫入字節數ssize_t nwrite = 0;//剩余未寫字節數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");//對方關閉文件描述符,返回已經寫完的字節數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){//發送數據numx = strlen(_packet.buf);//將本地字節轉化成網絡字節序_packet.len = htonl(numx);rc = writen(sockfd, &_packet, 4 + numx);if (rc == -1){return -1;} else if (rc < 4 + numx){return -1;}//接收數據memset(&_packet, 0, sizeof(_packet));//獲取包頭rc = readn(sockfd, &_packet.len, 4);if (rc == -1){return -1;} else if (rc < 4){return -1;}//將網絡字節轉化成本地字節numx = ntohl(_packet.len);//printf("接收數據的大小是%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;
}

?

轉載于:https://www.cnblogs.com/zhanggaofeng/p/6134757.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/456782.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/456782.shtml
英文地址,請注明出處:http://en.pswp.cn/news/456782.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解決selenium.common.exceptions.WebDriverException: Message: unknown error: call function result missin

(Session info: chrome73.0.3683.103)(Driver info: chromedriver2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41),platformWindows NT 10.0.17134 x86_64)報錯如上&#xff0c;由于版本不兼容 下面是谷歌瀏覽器與chromedriver的版本對應關系&#xff0c;供參考&#…

執行Hive語句報錯:FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user '

安裝個Hive真不省心&#xff0c;各種問題。最近安裝好Hive后執行Hive語句時碰到這樣的錯誤&#xff1a; hive> show databases; FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user rootlocalhost (using password: YES) NestedThr…

GPU

import tensorflow as tf a tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[2,3],namea) b tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[3,2],nameb) c tf.matmul(a,b)sess tf.Session(configtf.ConfigProto(log_device_placementTrue)) print sess.run(c)

阿里云部署django項目流程【centos7+python3+mysql】

購買阿里云服務器 到[阿里云官網]&#xff0c;選擇輕量應用服務器&#xff0c; 步驟如圖所示&#xff1a; 地域隨便選擇哪一個&#xff0c;鏡像的話&#xff0c;對比了CentOS&#xff0c;Debian&#xff0c;Ubuntu&#xff0c;我最終選擇了CentOS&#xff0c;因為流行嘛&…

XidianOJ 1123 K=1 Problem of Orz Pandas

題目描述 One panda named orz is playing a interesting game, he gets a big integer Num and an integer K. In this game, he can exchange two single numbers in Num. For example, he can get 1243 from 3241 by exchange 1 and 3.But orz can exchange at most K times…

對于頻繁的寫數據處理方式

添加一個新的表情的時候 調用 recentEmotions方法 將所有表情寫入數組 每次都是 添加一個新的表情進來 要將沙盒中的所有表情首先加載進數組&#xff0c;然后將表情添加到數組里面 然后在將數組寫入沙盒 處理方式 沒有必要每次都要到沙盒里面讀取數組文件 類方法 不能訪問 成員…

在Mysql中顯示所有用戶的操作教程(Linux環境下)

1.登錄數據庫 首先&#xff0c;你需要使用如下命令登錄到數據庫&#xff0c;注意&#xff0c;必須是root用戶哦~ mysql -u root -p 2.查詢用戶表 在Mysql中其實有一個內置且名為mysql的數據庫&#xff0c;這個數據庫中存儲的是Mysql的一些數據&#xff0c;比如用戶、權限信…

Scrapy 框架【學習筆記01】

Scrapy 框架 Scrapy是用純Python實現一個為了爬取網站數據、提取結構性數據而編寫的應用框架&#xff0c;用途非常廣泛。 框架的力量&#xff0c;用戶只需要定制開發幾個模塊就可以輕松的實現一個爬蟲&#xff0c;用來抓取網頁內容以及各種圖片&#xff0c;非常之方便。 Scra…

通過profile 用maven命令打不同配置的變量包

profiles定義如下<profiles><profile><id>local</id><properties><deploy.type>local</deploy.type></properties></profile><profile><id>dev</id><properties><deploy.type>dev</de…

執行Hive的查詢語句報錯:java.lang.IllegalArgumentException: Does not contain a valid host:port authority: loca

好不容易把Hive裝完了&#xff0c;結果一執行Hive的查詢語句運行MapReduce程序立馬報錯。。。 log詳細信息如下&#xff1a; Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 1; number of reducers: 0 2017-10-21 21:54:15,503…

scrapy startproject【學習筆記02】

入門案例 學習目標 創建一個Scrapy項目定義提取的結構化數據(Item)編寫爬取網站的 Spider 并提取出結構化數據(Item)編寫 Item Pipelines 來存儲提取到的Item(即結構化數據) 一. 新建項目(scrapy startproject) 在開始爬取之前&#xff0c;必須創建一個新的Scrapy項目。進入…

開始把其他的博客搬家到這里了

今天&#xff0c;用一晚上的時間進行一下文章的遷移吧。這樣以后查詢就可以在自己的博客中查找了&#xff0c;也算是給自己一個寫作的規律。 從很多個大牛的博客中都閱讀到&#xff0c;寫博客對于一個coder的重要性。希望這次可以堅持。轉載于:https://www.cnblogs.com/cyforev…

java系統高并發解決方案(轉載)

轉載博客地址&#xff1a;http://blog.csdn.net/zxl333/article/details/8685157 一個小型的網站&#xff0c;比如個人網站&#xff0c;可以使用最簡單的html靜態頁面就實現了&#xff0c;配合一些圖片達到美化效果&#xff0c;所有的頁面均存放在一個目錄下&#xff0c;這樣的…

Request/Response【學習筆記03】

Request Request 部分源碼&#xff1a; # 部分代碼 class Request(object_ref):def __init__(self, url, callbackNone, methodGET, headersNone, bodyNone, cookiesNone, metaNone, encodingutf-8, priority0,dont_filterFalse, errbackNone):self._encoding encoding # t…

TotoiseSVN的上手教程

本文轉自&#xff1a;http://www.cnblogs.com/xilentz/archive/2010/05/06/1728945.html TotoiseSVN的基本使用方法&#xff1a; 一、簽入源代碼到SVN服務器 假如我們使用Visual Studio在文件夾StartKit中創建了一個項目&#xff0c;我們要把這個項目的源代碼簽入到SVN Serv…

ALV可輸入狀態下輸入金額字段變小數的問題

http://blog.163.com/mxb_sapyeah/blog/static/10335262520167109022155/ 小數位數兩位 當我在給ALV上給該字段輸入整數 ‘1234 ‘ 時&#xff0c;該數據居然會默認變成‘12.34’ 可以在這里解決這個問題。就是定義字段目錄的時候&#xff0c;對于金額字段指定參考數據類型就…

Downloader Middlewares反反爬蟲【學習筆記04】

反反爬蟲相關機制 Some websites implement certain measures to prevent bots from crawling them, with varying degrees of sophistication. Getting around those measures can be difficult and tricky, and may sometimes require special infrastructure. Please consi…

【轉載】Android 關于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容問題

轉自&#xff1a;【歐陽鵬】http://blog.csdn.net/ouyang_peng Android 設備的CPU類型(通常稱為”ABIs”) armeabiv-v7a: 第7代及以上的 ARM 處理器。2011年15月以后的生產的大部分Android設備都使用它.arm64-v8a: 第8代、64位ARM處理器&#xff0c;很少設備&#xff0c;三星 G…

HDFS的簡介及基本操作(常用的命令參數介紹)

目錄前言&#xff1a;1、HDFS基本概念2、HDFS基本操作總結&#xff1a; 目錄 前言&#xff1a; 總算有空來接著寫大數據的學習筆記了&#xff0c;今天就把之前學過的HDFS的基礎知識詳細的介紹一下&#xff0c;如有哪點寫的不足希望大家多多指教。 1、HDFS基本概念 1.1、前…

Settings【學習筆記05】

Settings Scrapy設置(settings)提供了定制Scrapy組件的方法。可以控制包括核心(core)&#xff0c;插件(extension)&#xff0c;pipeline及spider組件。比如 設置Json Pipeliine、LOG_LEVEL等。 參考文檔&#xff1a;http://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/setti…