重點(diǎn):socket數(shù)據(jù)發(fā)送與接收問(wèn)題 數(shù)據(jù)的發(fā)送和接收是獨(dú)立的,并不是發(fā)送方執(zhí)行一次send,接收方就執(zhí)行以此recv。recv函數(shù)不分Send幾次,都會(huì)從輸入緩沖區(qū)盡可能多的Receive數(shù)據(jù)。如果發(fā)送方發(fā)送了多次信息,接收方?jīng)]來(lái)得及進(jìn)行recv,則數(shù)據(jù)堆積在輸入緩沖區(qū)中,取數(shù)據(jù)的時(shí)候會(huì)都取出來(lái)。換句話說(shuō),recv并不能判斷數(shù)據(jù)包的結(jié)束位置。 send函數(shù): 在數(shù)據(jù)進(jìn)行發(fā)送的時(shí)候,需要先檢查輸出緩沖區(qū)的可用空間大小,如果可用空間大小小于要發(fā)送的數(shù)據(jù)長(zhǎng)度,則send會(huì)被阻塞,直到緩沖區(qū)中的數(shù)據(jù)被發(fā)送到目標(biāo)主機(jī),有了足夠的空間之后,send函數(shù)才會(huì)將數(shù)據(jù)寫入輸出緩沖區(qū)。 TCP協(xié)議正在將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上的時(shí)候,輸出緩沖區(qū)會(huì)被鎖定(生產(chǎn)者消費(fèi)者問(wèn)題),不允許寫入,send函數(shù)會(huì)被阻塞,直到數(shù)據(jù)發(fā)送完,輸出緩沖區(qū)解鎖,此時(shí)send才能將數(shù)據(jù)寫入到輸出緩沖區(qū)。 要寫入的數(shù)據(jù)大于輸出緩沖區(qū)的最大長(zhǎng)度的時(shí)候,要分多次寫入,直到所有數(shù)據(jù)都被寫到緩沖區(qū)之后,send函數(shù)才會(huì)返回。 recv函數(shù): 函數(shù)先檢查輸入緩沖區(qū),如果輸入緩沖區(qū)中有數(shù)據(jù),讀取出緩沖區(qū)中的數(shù)據(jù),否則的話,recv函數(shù)會(huì)被阻塞,等待網(wǎng)絡(luò)上傳來(lái)數(shù)據(jù)。如果讀取的數(shù)據(jù)長(zhǎng)度小于輸出緩沖區(qū)中的數(shù)據(jù)長(zhǎng)度,沒(méi)法一次性將所有數(shù)據(jù)讀出來(lái),需要多次執(zhí)行recv函數(shù),才能將數(shù)據(jù)讀取完畢。 -------------------------------------------------------------------------------------------------------------------- 套接字的概念及分類 在網(wǎng)絡(luò)中,要全局的標(biāo)識(shí)一個(gè)參與通信的進(jìn)程,需要三元組:協(xié)議,IP地址以及端口號(hào)。要描述兩個(gè)應(yīng)用進(jìn)程之間的端到端的通信關(guān)聯(lián)需要五元組:協(xié)議,信源主機(jī)IP,信源應(yīng)用進(jìn)程端口,信宿主機(jī)IP,信宿應(yīng)用進(jìn)程端口。為了實(shí)現(xiàn)兩個(gè)應(yīng)用進(jìn)程的通信連接,提出了套接字的概念。套接字可以理解為通信連接的一端,將兩個(gè)套接字連接在一起,可以實(shí)現(xiàn)不同進(jìn)程之間的通信。 針對(duì)不同的通信需求,TCP/IP中提供了三種不同的套接字: (1)流套接字(SOCK_STREAM) 流套接字用于面向連接,可靠的數(shù)據(jù)傳輸服務(wù)。它之所以能實(shí)現(xiàn)可靠的數(shù)據(jù)服務(wù),是因?yàn)樗褂昧藗鬏斂刂茀f(xié)議–TCP。適合傳輸大量的數(shù)據(jù),但是不支持廣播和組播。 (2)數(shù)據(jù)報(bào)套接字(SOCK_DGRAM) 數(shù)據(jù)報(bào)套接字提供了一種無(wú)連接的服務(wù),通信雙方不需要建立任何顯示連接,數(shù)據(jù)可以發(fā)送到指定的套接字。數(shù)據(jù)報(bào)套接字使用UDP進(jìn)行數(shù)據(jù)傳輸,支持廣播和組播方式。 (3)原始套接字(SOCK_RAW) 原始套接字與標(biāo)準(zhǔn)套接字(上面兩個(gè))區(qū)別在于:原始套接字可以讀寫內(nèi)核沒(méi)有處理的IP數(shù)據(jù)報(bào),流套接字只能讀寫TCP數(shù)據(jù)報(bào),數(shù)據(jù)報(bào)套接字只能讀寫UDP數(shù)據(jù)報(bào)。原始套接字的主要目的是避開(kāi)TCP/IP的處理機(jī)制,被傳送的數(shù)據(jù)報(bào)可以直接傳送給需要他的程序。主要用于編寫自定義地層協(xié)議的應(yīng)用程序。 WinSock函數(shù) 1.WSAStartup函數(shù): 功能:用于初始化WinSock,即檢查系統(tǒng)中是否有Windows Sockets的實(shí)現(xiàn)庫(kù)。 格式:int WSAStartup(WORD wVersionRequest, LPWSADATA lpWSAData)。 參數(shù):wVersionRequest使用WinSock的最低版本號(hào),lpWSAData是WSADATA指針。 返回值:函數(shù)成功調(diào)用返回0,失敗時(shí)返回非0。 說(shuō)明:此函數(shù)是應(yīng)用程序調(diào)用的第一個(gè)WinSock函數(shù),只有在該函數(shù)調(diào)用成功后才能調(diào)用其他WinSock函數(shù)。 2.socket函數(shù): 功能:為應(yīng)用程序創(chuàng)建套接字。 格式:SOCKET socket(int af, int type, int protocol)。 參數(shù):af-套接字使用的協(xié)議地址族,如果使用TCP或者UDP,只能使用AF_INET;type-套接字協(xié)議類型,如SOCK_STREAM、SOCK_DGRAM;protocol-套接字使用的特定協(xié)議,如果不希望特別指定協(xié)議類型,則設(shè)置為0。 返回值:函數(shù)成功調(diào)用后返回一個(gè)新的套接字,是一個(gè)無(wú)符號(hào)的整型數(shù)據(jù);失敗時(shí)返回INVALID_SOCKET。 說(shuō)明:應(yīng)用程序在使用套接字通信之前,必須擁有一個(gè)套接字。 3.bind函數(shù): 功能:實(shí)現(xiàn)套接字與主機(jī)本地IP地址和端口號(hào)的綁定。 格式:int bind(SOCKET s, const struct sockaddr *name, int namelen)。 參數(shù):s-將要綁定的套接字;name-與指定協(xié)議有關(guān)的地址結(jié)構(gòu)指針;namelen-name參數(shù)的長(zhǎng)度。 返回值:函數(shù)成功時(shí)返回0;失敗時(shí)返回SOCKET_ERROR。 4.listen函數(shù): 功能:設(shè)定套接字為監(jiān)聽(tīng)狀態(tài),準(zhǔn)備接收由客戶機(jī)進(jìn)程發(fā)出的連接請(qǐng)求。 格式:int listen(SOCKET s, int backlog)。 參數(shù):s-已綁定地址,但還未建立連接的套接字標(biāo)識(shí)符;backlog-指定正在等待連接的最大隊(duì)列長(zhǎng)度。 返回值:函數(shù)成功時(shí)返回0;失敗時(shí)返回SOCKET_ERROR。 說(shuō)明:僅適用于面向連接的套接字,且用于服務(wù)器進(jìn)程。 5.connect函數(shù): 功能:提出與服務(wù)器建立連接的請(qǐng)求,如果服務(wù)器進(jìn)程接受請(qǐng)求,則服務(wù)器進(jìn)程與客戶機(jī)進(jìn)城之間便建立了一條通信連接。 格式:int connect(SOCKET s, const struct sockaddr FAR *name, int namelen )。 參數(shù):s-欲要建立連接的套接字;name-指向通信對(duì)方的套接字地址結(jié)構(gòu)指針,表示s欲與其建立連接;namelen-name參數(shù)的長(zhǎng)度。 返回值:函數(shù)成功時(shí)返回0;失敗時(shí)返回SOCKET_ERROR。 說(shuō)明:在客戶機(jī)進(jìn)程調(diào)用該方法請(qǐng)求建立連接時(shí),將激活建立連接的3次握手,以此來(lái)建立一條與服務(wù)器進(jìn)程的TCP連接。如果該函數(shù)調(diào)用之前沒(méi)有綁定地址,系統(tǒng)自動(dòng)綁定本地地址到此套接字。 6.accept函數(shù): 功能:接受客戶機(jī)進(jìn)程調(diào)用connect函數(shù)發(fā)出的連接請(qǐng)求。 格式:SOCKET accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)。 參數(shù):s-處于偵聽(tīng)狀態(tài)的套接字;addr-指向一個(gè)用來(lái)存放發(fā)出連接請(qǐng)求的客戶機(jī)進(jìn)程IP地址信息的地址結(jié)構(gòu)指針;addrlen-addr的長(zhǎng)度。 返回值:調(diào)用成功返回一個(gè)新的套接字,這個(gè)套接字對(duì)應(yīng)于已接受的那個(gè)客戶機(jī)進(jìn)程的連接,失敗時(shí)返回INVALID_SOCKET。 說(shuō)明:用于面向連接的服務(wù)器進(jìn)程,在IP協(xié)議族中只適用于TCP服務(wù)器端。 7.shutdown函數(shù): 功能:關(guān)閉套接字讀寫通道,即停止套接字接受傳遞的功能。 格式:int shutdown(SOCKET s, int how)。 參數(shù):s-套接字;how-描述停止哪些操作。 how-0:不再接收消息; how-1:不再允許發(fā)送消息; how-2:既不接收消息,也不再發(fā)送消息。 返回值:函數(shù)成功時(shí)返回0;失敗時(shí)返回SOCKET_ERROR。 說(shuō)明:只是停止套接字的功能,并沒(méi)有關(guān)閉套接字,套接字的資源還沒(méi)釋放。 8.closesocket函數(shù): 功能:關(guān)閉套接字,釋放與套接字關(guān)聯(lián)的所有資源。 格式:int closesocket(SOCKET s)。 參數(shù):s-將要關(guān)閉的套接字。 返回值:函數(shù)成功時(shí)返回0;失敗時(shí)返回SOCKET_ERROR。 說(shuō)明:當(dāng)套接字s的數(shù)據(jù)緩沖隊(duì)列中還有未發(fā)出的數(shù)據(jù)時(shí),如果套接字設(shè)定為SO_DONTLINGER,則等待數(shù)據(jù)緩沖隊(duì)列中的數(shù)據(jù)繼續(xù)傳輸完畢關(guān)閉該套接字;如果套接字設(shè)定為SO_LINGER,則分以下兩種情況: (1)Timeout設(shè)為0,套接字馬上關(guān)閉,數(shù)據(jù)緩沖隊(duì)列中數(shù)據(jù)丟失。 (2)Timeout不為0,等待數(shù)據(jù)傳輸完畢或者Timeout為0時(shí)關(guān)閉套接字。 9.WSACleanup函數(shù): 功能:終止使用WinSock,釋放為應(yīng)用程序分配的相關(guān)資源。 格式:int WSACleanup()。 參數(shù):無(wú)。 返回值:調(diào)用成功時(shí)返回0,失敗時(shí)返回非0。 說(shuō)明:該函數(shù)是任何一個(gè)基于WinSock應(yīng)用程序在最后必須調(diào)用的函數(shù),終止了所有Windows Sockets在所有線程上的操作。 10.recv函數(shù): 功能:在已建立連接的套接字上接收數(shù)據(jù)。 格式:int recv(SOCKET s, char *buf, int len, int flags)。 參數(shù):s-已建立連接的套接字;buf-存放接收到的數(shù)據(jù)的緩沖區(qū)指針;len-buf的長(zhǎng)度;flags-調(diào)用方式: (1)0:接收的是正常數(shù)據(jù),無(wú)特殊行為。 (2)MSG_PEEK:系統(tǒng)緩沖區(qū)數(shù)據(jù)復(fù)制到提供的接收緩沖區(qū),但是系統(tǒng)緩沖區(qū)內(nèi)容并沒(méi)有刪除。 (3)MSG_OOB:表示處理帶外數(shù)據(jù)。 返回值:接收成功時(shí)返回接收到的數(shù)據(jù)長(zhǎng)度,連接結(jié)束時(shí)返回0,連接失敗時(shí)返回SOCKET_ERROR。 11.send函數(shù): 功能:在已建立連接的套接字上發(fā)送數(shù)據(jù). 格式:int send(SOCKET s, const char *buf, int len, int flags)。 參數(shù):參數(shù):s-已建立連接的套接字;buf-存放將要發(fā)送的數(shù)據(jù)的緩沖區(qū)指針;len-發(fā)送緩沖區(qū)中的字符數(shù);flags-控制數(shù)據(jù)傳輸方式: (1)0:接收的是正常數(shù)據(jù),無(wú)特殊行為。 (2)MSG_DONTROUTE:表示目標(biāo)主機(jī)就在本地網(wǎng)絡(luò)中,無(wú)需路由選擇。 (3)MSG_OOB:表示處理帶外數(shù)據(jù)。 返回值:發(fā)送成功時(shí)返回發(fā)送的數(shù)據(jù)長(zhǎng)度,連接結(jié)束時(shí)返回0,連接失敗時(shí)返回SOCKET_ERROR。 -------------------------------------------------------------------------------------------------------------------- socket緩沖區(qū) 每一個(gè)socket在被創(chuàng)建之后,系統(tǒng)都會(huì)給它分配兩個(gè)緩沖區(qū),即輸入緩沖區(qū)和輸出緩沖區(qū)。 這里寫圖片描述 send函數(shù)并不是直接將數(shù)據(jù)傳輸?shù)骄W(wǎng)絡(luò)中,而是負(fù)責(zé)將數(shù)據(jù)寫入輸出緩沖區(qū),數(shù)據(jù)從輸出緩沖區(qū)發(fā)送到目標(biāo)主機(jī)是由TCP協(xié)議完成的。數(shù)據(jù)寫入到輸出緩沖區(qū)之后,send函數(shù)就可以返回了,數(shù)據(jù)是否發(fā)送出去,是否發(fā)送成功,何時(shí)到達(dá)目標(biāo)主機(jī),都不由它負(fù)責(zé)了,而是由協(xié)議負(fù)責(zé)。 recv函數(shù)也是一樣的,它并不是直接從網(wǎng)絡(luò)中獲取數(shù)據(jù),而是從輸入緩沖區(qū)中讀取數(shù)據(jù)。 輸入輸出緩沖區(qū),系統(tǒng)會(huì)為每個(gè)socket都單獨(dú)分配,并且是在socket創(chuàng)建的時(shí)候自動(dòng)生成的。一般來(lái)說(shuō),默認(rèn)的輸入輸出緩沖區(qū)大小為8K。套接字關(guān)閉的時(shí)候,輸出緩沖區(qū)的數(shù)據(jù)不會(huì)丟失,會(huì)由協(xié)議發(fā)送到另一方;而輸入緩沖區(qū)的數(shù)據(jù)則會(huì)丟失。 socket數(shù)據(jù)發(fā)送與接收問(wèn)題 數(shù)據(jù)的發(fā)送和接收是獨(dú)立的,并不是發(fā)送方執(zhí)行一次send,接收方就執(zhí)行以此recv。recv函數(shù)不管發(fā)送幾次,都會(huì)從輸入緩沖區(qū)盡可能多的獲取數(shù)據(jù)。如果發(fā)送方發(fā)送了多次信息,接收方?jīng)]來(lái)得及進(jìn)行recv,則數(shù)據(jù)堆積在輸入緩沖區(qū)中,取數(shù)據(jù)的時(shí)候會(huì)都取出來(lái)。換句話說(shuō),recv并不能判斷數(shù)據(jù)包的結(jié)束位置。 send函數(shù): 在數(shù)據(jù)進(jìn)行發(fā)送的時(shí)候,需要先檢查輸出緩沖區(qū)的可用空間大小,如果可用空間大小小于要發(fā)送的數(shù)據(jù)長(zhǎng)度,則send會(huì)被阻塞,直到緩沖區(qū)中的數(shù)據(jù)被發(fā)送到目標(biāo)主機(jī),有了足夠的空間之后,send函數(shù)才會(huì)將數(shù)據(jù)寫入輸出緩沖區(qū)。 TCP協(xié)議正在將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上的時(shí)候,輸出緩沖區(qū)會(huì)被鎖定(生產(chǎn)者消費(fèi)者問(wèn)題),不允許寫入,send函數(shù)會(huì)被阻塞,直到數(shù)據(jù)發(fā)送完,輸出緩沖區(qū)解鎖,此時(shí)send才能將數(shù)據(jù)寫入到輸出緩沖區(qū)。 要寫入的數(shù)據(jù)大于輸出緩沖區(qū)的最大長(zhǎng)度的時(shí)候,要分多次寫入,直到所有數(shù)據(jù)都被寫到緩沖區(qū)之后,send函數(shù)才會(huì)返回。 recv函數(shù): 函數(shù)先檢查輸入緩沖區(qū),如果輸入緩沖區(qū)中有數(shù)據(jù),讀取出緩沖區(qū)中的數(shù)據(jù),否則的話,recv函數(shù)會(huì)被阻塞,等待網(wǎng)絡(luò)上傳來(lái)數(shù)據(jù)。如果讀取的數(shù)據(jù)長(zhǎng)度小于輸出緩沖區(qū)中的數(shù)據(jù)長(zhǎng)度,沒(méi)法一次性將所有數(shù)據(jù)讀出來(lái),需要多次執(zhí)行recv函數(shù),才能將數(shù)據(jù)讀取完畢。---轉(zhuǎn)發(fā)于網(wǎng)絡(luò)。 循環(huán)發(fā)送和接收防止send或者 recv不完整,這樣你想發(fā)一個(gè)預(yù)定義函數(shù)<< 幾MB直接調(diào)用下面方法就okay,不會(huì)少發(fā)~ // 循環(huán)發(fā)送和接收
//防止send或者 recv不完整,這樣你想發(fā)一個(gè)
//幾MB直接調(diào)用下面方法就okay,不會(huì)少發(fā)~
bool SendAll(SOCKET &sock,char*buffer,int size)
{
while(size>0)
{
int SendSize=send(sock,buffer,size,0);
if(SOCKET_ERROR==SendSize)
return false;
size=size-SendSize;//用于循環(huán)發(fā)送且忒出功能
buffer+=SendSize;//用于計(jì)算已發(fā)buffer的偏移量
}
return true;
}
bool RecvAll(SOCKET &sock,char*buffer,int size)
{
while(size>0)//剩余部分大于0
int RecvSize=recv(sock,buffer,size,0);
if(SOCKET_ERROR==SendSize)
return false;
size=size-SendSize;//用于循環(huán)發(fā)送且忒出功能
buffer+=SendSize;//用于計(jì)算已發(fā)buffer的偏移量
}
return true;
}
|