日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > 互聯(lián)網(wǎng) > Socket編程實(shí)踐(6) --TCP服務(wù)器常見(jiàn)問(wèn)題(1)

Socket編程實(shí)踐(6) --TCP服務(wù)器常見(jiàn)問(wèn)題(1)

來(lái)源:程序員人生   發(fā)布時(shí)間:2014-12-17 08:21:14 閱讀次數(shù):2805次

流協(xié)議與粘包

粘包的表現(xiàn)

Host A 發(fā)送數(shù)據(jù)給 Host B; 而Host B 接收數(shù)據(jù)的方式不肯定


 



粘包產(chǎn)生的緣由 

 



說(shuō)明

TCP

字節(jié)流,無(wú)邊界

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

UDP

數(shù)據(jù)報(bào),有邊界

對(duì)方接受數(shù)據(jù)包的個(gè)數(shù)是不肯定的

 

 

產(chǎn)生粘包問(wèn)題的緣由分析

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

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

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

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

    5、tcp延遲發(fā)送機(jī)制等

結(jié)論:tcp/ip協(xié)議,在傳輸層沒(méi)有處理粘包問(wèn)題。

 

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

  定長(zhǎng)包

  包尾加 (ftp)

  包頭加上包體長(zhǎng)度(以下)

  更復(fù)雜的利用層協(xié)議

 

編程實(shí)踐-readn && writen

管道,FIFO和某些裝備(特別是終端和網(wǎng)絡(luò))有以下兩種性質(zhì):

    1)1次read操作所返回的數(shù)據(jù)可能少于所要求的數(shù)據(jù),即便還沒(méi)到達(dá)文件尾端也可能這樣,但這不是1個(gè)毛病,應(yīng)當(dāng)繼續(xù)讀該裝備;

    2)1次write操作的返回值也可能少于指定輸入的字節(jié)數(shù).這多是由于某個(gè)因素釀成的,如:內(nèi)核緩沖區(qū)滿...但這也不是1個(gè)毛病,應(yīng)當(dāng)繼續(xù)寫(xiě)余下的數(shù)據(jù)(通常,只有非阻塞描寫(xiě)符,或捕捉到1個(gè)信號(hào)時(shí),才產(chǎn)生這類write的中途返回)

     在讀寫(xiě)磁盤(pán)文件時(shí)從未見(jiàn)到過(guò)這類情況,除非是文件系統(tǒng)用完了空間,或接近了配額限制,不能將所要求寫(xiě)的數(shù)據(jù)全部寫(xiě)出!

 

   通常,在讀,寫(xiě)1個(gè)網(wǎng)絡(luò)裝備,管道或終端時(shí),需要斟酌這些特性.因而,我們就有了下面的這兩個(gè)函數(shù):readn和writen,功能分別是讀寫(xiě)指定的N字節(jié)數(shù)據(jù),并處理返回值可能小于要求值的情況:

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


返回值:

    讀寫(xiě)的字節(jié)數(shù);若出錯(cuò),返回⑴

 

實(shí)現(xiàn):

    這兩個(gè)函數(shù)只是按需屢次調(diào)用read和write系統(tǒng)調(diào)用直至讀寫(xiě)了N個(gè)數(shù)據(jù)

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點(diǎn)東西都沒(méi)讀 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點(diǎn)東西都沒(méi)寫(xiě) 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; }

報(bào)頭加上報(bào)文長(zhǎng)度編程實(shí)踐

報(bào)文結(jié)構(gòu):

struct TransStruct { int m_length; //報(bào)頭:保存數(shù)據(jù)m_text的真實(shí)數(shù)據(jù)長(zhǎng)度 char m_text[BUFSIZ]; //報(bào)文:保存真正要發(fā)送的數(shù)據(jù) };

 

   發(fā)報(bào)文時(shí):前4個(gè)字節(jié)長(zhǎng)度+報(bào)文

   收?qǐng)?bào)文時(shí):先讀前4個(gè)字節(jié),求出長(zhǎng)度;根據(jù)長(zhǎng)度讀數(shù)據(jù)。

//server端完全代碼及解析 #include "commen.h" //echo 服務(wù)器writen,readn 版 int main() { int sockfd = socket(AF_INET,SOCK_STREAM,0); if (sockfd == ⑴) { err_exit("socket error"); } //添加地址復(fù)用 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; //綁定本機(jī)的任意1個(gè)IP地址 if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == ⑴) { err_exit("bind error"); } //啟動(dòng)監(jiān)聽(tīng)套接字 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個(gè)客戶端連接進(jìn)來(lái),就fork1個(gè)子進(jìn)程, //相應(yīng)的業(yè)務(wù)處理由子進(jìn)程完成,父進(jìn)程繼續(xù)監(jiān)聽(tīng) pid_t pid = fork(); if (pid == ⑴) { close(sockfd); close(peerSockfd); err_exit("fork error"); } else if (pid == 0) //子進(jìn)程,處理業(yè)務(wù) { close(sockfd); //子進(jìn)程關(guān)閉監(jiān)聽(tīng)套接字,由于子進(jìn)程不負(fù)責(zé)監(jiān)聽(tīng)?wèi){務(wù) struct TransStruct recvBuf; ssize_t readCount = 0; while (true) { memset(&recvBuf,0,sizeof(recvBuf)); //首先,從客戶端讀取報(bào)頭長(zhǎng)度 if ((readCount = readn(peerSockfd,&(recvBuf.m_length),4)) == ⑴) { err_exit("readn error"); } else if (readCount == 0) //如果鏈接關(guān)閉 { peerClosePrint("client connect closed"); } //根據(jù)報(bào)文實(shí)際長(zhǎng)度,讀取數(shù)據(jù) if ((readCount = readn(peerSockfd,&(recvBuf.m_text),recvBuf.m_length)) == ⑴) { err_exit("readn error"); } else if (readCount == 0) { peerClosePrint("client connect closed"); } //將整體報(bào)文回寫(xiě)回客戶端 if (writen(peerSockfd,&recvBuf,recvBuf.m_length+4) == ⑴) { err_exit("writen error"); } recvBuf.m_text[recvBuf.m_length] = 0; //寫(xiě)至終端 fputs(recvBuf.m_text,stdout); } } else if (pid > 0) //父進(jìn)程 { close(peerSockfd); } } close(sockfd); return 0; }

//client端完全代碼實(shí)現(xiàn)及解析 #include "commen.h" int main() { int sockfd = socket(AF_INET,SOCK_STREAM,0); if (sockfd == ⑴) { err_exit("socket error"); } //填寫(xiě)好服務(wù)器地址及其端口號(hào) 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; //從鍵盤(pán)輸入數(shù)據(jù) while (fgets(sendBuf.m_text,sizeof(sendBuf.m_text),stdin) != NULL) { //保存的是真實(shí)報(bào)文的長(zhǎng)度 sendBuf.m_length = strlen(sendBuf.m_text); //向server發(fā)送數(shù)據(jù)....+4的緣由:需要添加報(bào)首的4個(gè)字節(jié)報(bào)頭的長(zhǎng)度 if (writen(sockfd,&sendBuf,sendBuf.m_length+4) == ⑴) { err_exit("write socket error"); } //首先,從server端接收將要發(fā)送的數(shù)據(jù)報(bào)的長(zhǎng)度 if ((readCount = readn(sockfd,&(recvBuf.m_length),4)) == ⑴) { err_exit("read socket error"); } else if (readCount == 0) { peerClosePrint("client connect closed"); } //然后,根據(jù)從server端讀來(lái)的報(bào)文長(zhǎng)度,讀取報(bào)文 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; //將其回寫(xiě)到終端 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; //報(bào)文結(jié)構(gòu) struct TransStruct { int m_length; //報(bào)頭:保存數(shù)據(jù)m_text的真實(shí)數(shù)據(jù)長(zhǎng)度 char m_text[BUFSIZ]; //報(bào)文:保存真正要發(fā)送的數(shù)據(jù) }; //出錯(cuò)退出 void err_exit(std::string str) { perror(str.c_str()); exit(EXIT_FAILURE); } //對(duì)端關(guān)閉鏈接退出 void peerClosePrint(std::string str = "peer connect closed") { cout << str << endl; _exit(0); } //信號(hào)捕獲函數(shù):上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; } } //經(jīng)典的readn函數(shù)(來(lái)源: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點(diǎn)東西都沒(méi)讀 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; } //經(jīng)典的writen函數(shù)(來(lái)源: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點(diǎn)東西都沒(méi)寫(xiě) 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

生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 精品少妇一区二区三区视频免付费 | 国产99视频精品免费视频7 | 午夜在线影院 | 亚洲国产一区二区视频 | 国产精品久久久久久久久久不蜜月 | 日韩在线视频一区二区三区 | 国产精品久久久久一区二区三区 | 久久精品视频网 | 在线看的毛片 | 欧美二三四区 | 欧美亚洲日本国产 | 久久久久国产精品午夜一区 | 综合久久久久 | 国产精品久久久久婷婷二区次 | 亚洲成人av电影 | ...99久久国产成人免费精品 | 亚洲乱码在线 | 欧美精品在线免费观看 | 亚洲xxxx视频 | 国产一区二区精品在线 | 99精品国产在热久久婷婷 | 久久69| 日韩一二区 | 91亚洲成a人片在线观看www | 91看片官网 | 污黄网站 | 久久久精品国产免费观看一区二区 | 日本久久精 | 国产三级网站 | 成人精品国产免费网站 | 久久精品三级 | 国产一区二区免费在线观看 | 高清久久久| 国产成人精品免费视频大全最热 | 亚洲欧美一区二 | 国产精品正在播放 | 亚洲欧美色图片 | 欧美在线三区 | 国产精品婷婷久久久久 | 国产第一亚洲 | 国产精品日韩精品 |