多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > 互聯網 > Socket編程實踐(6) --TCP服務器常見問題(1)

Socket編程實踐(6) --TCP服務器常見問題(1)

來源:程序員人生   發布時間:2014-12-17 08:21:14 閱讀次數:2801次

流協議與粘包

粘包的表現

Host A 發送數據給 Host B; 而Host B 接收數據的方式不肯定


 



粘包產生的緣由 

 



說明

TCP

字節流,無邊界

對等方,1次讀操作,不能保證完全把消息讀完

UDP

數據報,有邊界

對方接受數據包的個數是不肯定的

 

 

產生粘包問題的緣由分析

    1、SQ_SNDBUF 套接字本身有緩沖區 (發送緩沖區、接受緩沖區)

    2、tcp傳送的端 mss大小限制

    3、鏈路層也有MTU大小限制,如果數據包大于>MTU要在IP層進行分片,致使消息分割。

    4、tcp的流量控制和堵塞控制,也可能致使粘包

    5、tcp延遲發送機制等

結論:tcp/ip協議,在傳輸層沒有處理粘包問題。

 

粘包解決方案(本質上是要在利用層保護消息與消息的邊界)

  定長包

  包尾加 (ftp)

  包頭加上包體長度(以下)

  更復雜的利用層協議

 

編程實踐-readn && writen

管道,FIFO和某些裝備(特別是終端和網絡)有以下兩種性質:

    1)1次read操作所返回的數據可能少于所要求的數據,即便還沒到達文件尾端也可能這樣,但這不是1個毛病,應當繼續讀該裝備;

    2)1次write操作的返回值也可能少于指定輸入的字節數.這多是由于某個因素釀成的,如:內核緩沖區滿...但這也不是1個毛病,應當繼續寫余下的數據(通常,只有非阻塞描寫符,或捕捉到1個信號時,才產生這類write的中途返回)

     在讀寫磁盤文件時從未見到過這類情況,除非是文件系統用完了空間,或接近了配額限制,不能將所要求寫的數據全部寫出!

 

   通常,在讀,寫1個網絡裝備,管道或終端時,需要斟酌這些特性.因而,我們就有了下面的這兩個函數:readn和writen,功能分別是讀寫指定的N字節數據,并處理返回值可能小于要求值的情況:

ssize_t readnint fd, void *buf, size_t count); ssize_t writen(int fd, const void *buf, size_t count);


返回值:

    讀寫的字節數;若出錯,返回⑴

 

實現:

    這兩個函數只是按需屢次調用read和write系統調用直至讀寫了N個數據

ssize_t readn(int fd,void *buf,size_t count) { size_t nLeft = count; ssize_t nRead = 0; char *ptr = static_cast<char *>(buf); while (nLeft > 0) { if ((nRead = read(fd,ptr,nLeft)) < 0) { //1點東西都沒讀 if (nLeft == count) { return ⑴; //error } else { break; //error, return amount read so far } } else if (nRead == 0) { break; //EOF } nLeft -= nRead; ptr += nRead; } return count - nLeft; } ssize_t writen(int fd, const void *buf, size_t count) { size_t nLeft = count; ssize_t nWritten; const char *ptr = static_cast<const char *>(buf); while (nLeft > 0) { if ((nWritten = write(fd,ptr,nLeft)) < 0) { //1點東西都沒寫 if (nLeft == count) { return ⑴; //error } else { break; //error, return amount write so far } } else if (nWritten == 0) { break; //EOF } nLeft -= nWritten; ptr += nWritten; } return count - nWritten; }

報頭加上報文長度編程實踐

報文結構:

struct TransStruct { int m_length; //報頭:保存數據m_text的真實數據長度 char m_text[BUFSIZ]; //報文:保存真正要發送的數據 };

 

   發報文時:前4個字節長度+報文

   收報文時:先讀前4個字節,求出長度;根據長度讀數據。

//server端完全代碼及解析 #include "commen.h" //echo 服務器writen,readn 版 int main() { int sockfd = socket(AF_INET,SOCK_STREAM,0); if (sockfd == ⑴) { err_exit("socket error"); } //添加地址復用 int optval = 1; if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == ⑴) { err_exit("setsockopt SO_REUSEADDR error"); } //綁定 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8002); serverAddr.sin_addr.s_addr = INADDR_ANY; //綁定本機的任意1個IP地址 if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == ⑴) { err_exit("bind error"); } //啟動監聽套接字 if (listen(sockfd,SOMAXCONN) == ⑴) { err_exit("listen error"); } struct sockaddr_in peerAddr; socklen_t peerLen = sizeof(peerAddr); while (true) { //接受鏈接 int peerSockfd = accept(sockfd, (struct sockaddr *)&peerAddr,&peerLen); if (peerSockfd == ⑴) { err_exit("accept error"); } //打印客戶信息 cout << "Client:" << endl; cout << " sin_port: " << ntohs(peerAddr.sin_port) << endl; cout << " sin_addr: " << inet_ntoa(peerAddr.sin_addr) << endl; cout << " socket: " << peerSockfd << endl; //每有1個客戶端連接進來,就fork1個子進程, //相應的業務處理由子進程完成,父進程繼續監聽 pid_t pid = fork(); if (pid == ⑴) { close(sockfd); close(peerSockfd); err_exit("fork error"); } else if (pid == 0) //子進程,處理業務 { close(sockfd); //子進程關閉監聽套接字,由于子進程不負責監聽憑務 struct TransStruct recvBuf; ssize_t readCount = 0; while (true) { memset(&recvBuf,0,sizeof(recvBuf)); //首先,從客戶端讀取報頭長度 if ((readCount = readn(peerSockfd,&(recvBuf.m_length),4)) == ⑴) { err_exit("readn error"); } else if (readCount == 0) //如果鏈接關閉 { peerClosePrint("client connect closed"); } //根據報文實際長度,讀取數據 if ((readCount = readn(peerSockfd,&(recvBuf.m_text),recvBuf.m_length)) == ⑴) { err_exit("readn error"); } else if (readCount == 0) { peerClosePrint("client connect closed"); } //將整體報文回寫回客戶端 if (writen(peerSockfd,&recvBuf,recvBuf.m_length+4) == ⑴) { err_exit("writen error"); } recvBuf.m_text[recvBuf.m_length] = 0; //寫至終端 fputs(recvBuf.m_text,stdout); } } else if (pid > 0) //父進程 { close(peerSockfd); } } close(sockfd); return 0; }

//client端完全代碼實現及解析 #include "commen.h" int main() { int sockfd = socket(AF_INET,SOCK_STREAM,0); if (sockfd == ⑴) { err_exit("socket error"); } //填寫好服務器地址及其端口號 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8002); serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == ⑴) { err_exit("connect error"); } int readCount = 0; struct TransStruct sendBuf; struct TransStruct recvBuf; //從鍵盤輸入數據 while (fgets(sendBuf.m_text,sizeof(sendBuf.m_text),stdin) != NULL) { //保存的是真實報文的長度 sendBuf.m_length = strlen(sendBuf.m_text); //向server發送數據....+4的緣由:需要添加報首的4個字節報頭的長度 if (writen(sockfd,&sendBuf,sendBuf.m_length+4) == ⑴) { err_exit("write socket error"); } //首先,從server端接收將要發送的數據報的長度 if ((readCount = readn(sockfd,&(recvBuf.m_length),4)) == ⑴) { err_exit("read socket error"); } else if (readCount == 0) { peerClosePrint("client connect closed"); } //然后,根據從server端讀來的報文長度,讀取報文 if ((readCount = readn(sockfd,&(recvBuf.m_text),recvBuf.m_length)) == ⑴) { err_exit("read socket error"); } else if (readCount == 0) { peerClosePrint("client connect closed"); } recvBuf.m_text[recvBuf.m_length] = 0; //將其回寫到終端 fputs(recvBuf.m_text,stdout); memset(&sendBuf,0,sizeof(sendBuf)); memset(&recvBuf,0,sizeof(recvBuf)); } close(sockfd); return 0; }

-commen.h完全代碼及解析

#ifndef COMMEN_H_INCLUDED #define COMMEN_H_INCLUDED #include <unistd.h> #include <signal.h> #include <errno.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/msg.h> #include <sys/sem.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; //報文結構 struct TransStruct { int m_length; //報頭:保存數據m_text的真實數據長度 char m_text[BUFSIZ]; //報文:保存真正要發送的數據 }; //出錯退出 void err_exit(std::string str) { perror(str.c_str()); exit(EXIT_FAILURE); } //對端關閉鏈接退出 void peerClosePrint(std::string str = "peer connect closed") { cout << str << endl; _exit(0); } //信號捕獲函數:上1篇博客中的代碼需要使用的 void onSignal(int signalNumber) { switch (signalNumber) { case SIGUSR1: cout << "child receive SIGUSR1" << signalNumber << endl; _exit(0); case SIGUSR2: cout << "parent receive SIGUSR2: " << signalNumber << endl; _exit(0); default: cout << "RECV OTHRER SIGNAL" << endl; } } //經典的readn函數(來源:APUE) ssize_t readn(int fd,void *buf,size_t count) { size_t nLeft = count; ssize_t nRead = 0; char *ptr = static_cast<char *>(buf); while (nLeft > 0) { if ((nRead = read(fd,ptr,nLeft)) < 0) { //1點東西都沒讀 if (nLeft == count) { return ⑴; //error } else { break; //error, return amount read so far } } else if (nRead == 0) { break; //EOF } nLeft -= nRead; ptr += nRead; } return count - nLeft; } //經典的writen函數(來源:APUE) ssize_t writen(int fd, const void *buf, size_t count) { size_t nLeft = count; ssize_t nWritten; const char *ptr = static_cast<const char *>(buf); while (nLeft > 0) { if ((nWritten = write(fd,ptr,nLeft)) < 0) { //1點東西都沒寫 if (nLeft == count) { return ⑴; //error } else { break; //error, return amount write so far } } else if (nWritten == 0) { break; //EOF } nLeft -= nWritten; ptr += nWritten; } return count - nWritten; } #endif // COMMEN_H_INCLUDED

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 欧美成人在线影院 | 一区二区三区欧美视频 | 欧美性猛交xxx乱大交 | 久草国产精品 | 国产精品亚洲欧美日韩区 | 男人和女人全黄一级毛片 | 欧美精品网站 | 国产免费播放一区二区 | 精品视频69v精品视频 | a丫久久久久久一级毛片 | 国产欧美日韩一区二区三区 | 亚洲视频欧美 | 精品久久久久久综合网 | 亚洲美女又黄又爽在线观看 | freexx性日本| 国产亚洲综合成人91精品 | 欧美自拍在线 | 亚洲成人影院在线观看 | 自拍偷拍一区 | 图片区小说区欧洲区 | 亚洲在线视频 | 最近最新中文字幕免费大全3 | 2020国产精品 | 免费区欧美一级毛片精品 | 免费一区二区三区四区 | 在线观看网 | 欧美成人精品第一区 | 日韩永久在线观看免费视频 | 国产精品欧美亚洲韩国日本不卡 | 最新中文字幕 | 欧美一级第一免费高清 | 黄色a大片| 老司机免费午夜精品视频 | 色综合久久久高清综合久久久 | 伊人影院网 | 亚洲在线网址 | 中文精品久久久久国产不卡 | 五月天精品视频播放在线观看 | 久久91精品国产一区二区 | 欧美最猛黑人xxxx黑人猛交3p | 周妍希国产福利在线观看 |