最簡單的TCP、UDP案例及各函式的詳細解釋
TCP:
server
#include "stdafx.h" #include<iostream> #define BUF_SZIE 64 #include "winsock2.h" #pragma comment(lib, "ws2_32.lib") using namespace std; int main(int argc, char* argv[]) { WSADATA wsd; //WSADATA變數 SOCKET sServer; //伺服器套接字 SOCKET sClient; //客戶端套接字 SOCKADDR_IN addrServ;; //伺服器地址 SOCKADDR_IN只是SOCKADDR在TCP/IP協議下的一個特例 char buf[BUF_SZIE]; //接收資料緩衝區 int retVal; //返回值 //初始化套結字動態庫,載入Windows Socket動態庫DLL if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)//wsd返回被載入動態庫的有關資訊 { cout<<"WSAStartup failed!\n"; return 1; } if(LOBYTE(wsd.wVersion)!=2||HIBYTE(wsd.wHighVersion)!=2) { WSACleanup();//釋放套接字資源; return -1; } //建立套接字 sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == sServer) { cout<<"socket failed!\n"; WSACleanup();//釋放套接字資源; return -1; } //伺服器套接字地址 addrServ.sin_family = AF_INET; addrServ.sin_port = htons(4999); //從主機位元組順序轉換為網路位元組順序 host to net long/short 相關函式htonl ntohl() ntohs() addrServ.sin_addr.s_addr = INADDR_ANY; //繫結套接字 retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN)); if(SOCKET_ERROR == retVal) { cout<<"bind failed!\n"; closesocket(sServer); //關閉套接字 WSACleanup(); //釋放套接字資源; return -1; } //開始監聽 retVal = listen(sServer, 1); if(SOCKET_ERROR == retVal) { cout<<"listen failed!\n"; closesocket(sServer); //關閉套接字 WSACleanup(); //釋放套接字資源; return -1; } //接受客戶端請求 sockaddr_in addrClient; int addrClientlen = sizeof(addrClient); sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen); if(INVALID_SOCKET == sClient) { cout<<"accept failed!\n"; closesocket(sServer); //關閉套接字 WSACleanup(); //釋放套接字資源; return -1; } //接收客戶端資料 ZeroMemory(buf, BUF_SZIE); retVal = recv(sClient, buf, BUF_SZIE, 0); if (SOCKET_ERROR == retVal) { cout<<"recv failed!\n"; closesocket(sServer); //關閉套接字 closesocket(sClient); //關閉套接字 WSACleanup(); //釋放套接字資源; return -1; } cout<< buf; //輸出"MyTCP" //退出 closesocket(sServer); //關閉套接字,同時關閉網路連線 closesocket(sClient); //關閉套接字 WSACleanup(); //釋放套接字資源; system("pause"); return 0; }
client
#include "stdafx.h" #include<iostream> #define BUF_SZIE 64 #include "winsock2.h" #pragma comment(lib, "ws2_32.lib") using namespace std; int main(int argc, char* argv[]) { WSADATA wsd; //WSADATA變數 SOCKET sHost; //伺服器套接字 SOCKADDR_IN servAddr; //伺服器地址 char buf[BUF_SZIE]; //接收資料緩衝區 int retVal; //返回值 //初始化套結字動態庫 if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { cout<<"WSAStartup failed!\n"; return -1; } //建立套接字 sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == sHost) { cout<<"socket failed!\n"; WSACleanup();//釋放套接字資源 return -1; } //設定伺服器地址 servAddr.sin_family =AF_INET; servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //實現網路地址和“.”間隔字串地址之間的轉換 //char* pAddr=inet_ntoa(*(in_addr*)(&servAddr.sin_addr.s_addr)); servAddr.sin_port = htons((short)4999); int nServAddlen = sizeof(servAddr); //連線伺服器 retVal=connect(sHost,(LPSOCKADDR)&servAddr, sizeof(servAddr)); if(SOCKET_ERROR == retVal) { cout<<"connect failed!\n"; closesocket(sHost); //關閉套接字 WSACleanup(); //釋放套接字資源 return -1; } //向伺服器傳送資料 ZeroMemory(buf, BUF_SZIE); strcpy(buf, "MyTCP"); retVal = send(sHost, buf, strlen(buf), 0); if (SOCKET_ERROR == retVal) { cout<<"send failed!\n"; closesocket(sHost); //關閉套接字 WSACleanup(); //釋放套接字資源 return -1; } //退出 closesocket(sHost); //關閉套接字 WSACleanup(); //釋放套接字資源 system("pause"); return 0; }
UDP:
server
#include "stdafx.h" #include<iostream> #define BUF_SZIE 64 #include "winsock2.h" #pragma comment(lib, "ws2_32.lib") using namespace std; int main(int argc, char* argv[]) { WSADATA wsd; //WSADATA變數 SOCKET s; //套接字 SOCKADDR_IN servAddr; //伺服器地址 char buf[BUF_SZIE]; //接收資料緩衝區 //初始化套結字動態庫 if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { cout<<"WSAStartup failed!\n"; return 1; } //建立套接字 s = socket(AF_INET, SOCK_DGRAM, 0); if (s == INVALID_SOCKET) { cout<<"socket() failed; %d\n"<<WSAGetLastError(); WSACleanup();//釋放套接字資源 return 1; } int nErrCode; //返回值 int nBufLen; //接收資料緩衝區大小 int nOptlen = sizeof(nBufLen); //獲取接收資料緩衝區大小 nErrCode = getsockopt(s, SOL_SOCKET, SO_RCVBUF,(char*)&nBufLen, &nOptlen); if (SOCKET_ERROR == nErrCode) { //處理失敗 } //設定接收資料緩衝區為原來的10倍 nBufLen *= 10; nErrCode = setsockopt(s, SOL_SOCKET, SO_RCVBUF,(char*)&nBufLen, nOptlen); if (SOCKET_ERROR == nErrCode) { //失敗處理 } //檢查設定系統接收資料緩衝區是否成功 int uiNewRcvBuf; getsockopt(s, SOL_SOCKET, SO_RCVBUF,(char*)&uiNewRcvBuf, &nOptlen); if (SOCKET_ERROR == nErrCode || uiNewRcvBuf != nBufLen) { //失敗處理 } //伺服器地址 servAddr.sin_family = AF_INET; servAddr.sin_port = htons((short)5000); //埠 servAddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP //繫結 if (bind(s, (SOCKADDR *)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) { cout<<"bind() failed: %d\n"<<WSAGetLastError(); closesocket(s); //關閉套接字 WSACleanup(); //釋放套接字資源 return 1; } //接收資料 SOCKADDR_IN clientAddr; int nClientLen = sizeof(clientAddr); ZeroMemory(buf, BUF_SZIE); if(recvfrom(s, buf, BUF_SZIE, 0, (SOCKADDR*)&clientAddr, &nClientLen) == SOCKET_ERROR) { cout<<"recvfrom() failed: %d\n"<<WSAGetLastError(); closesocket(s); //關閉套接字 WSACleanup(); //釋放套接字資源 return 1; } cout<<buf;//輸出"MyUDP" closesocket(s); //關閉套接字 WSACleanup(); //釋放套接字資源 system("pause"); return 0; }
client
#include "stdafx.h"
#include<iostream>
#define BUF_SZIE 64
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main(int argc, char* argv[])
{
WSADATA wsd; //WSADATA變數
SOCKET s; //套接字
SOCKADDR_IN servAddr; //伺服器地址
char buf[BUF_SZIE]; //傳送資料緩衝區
//初始化套結字動態庫
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
cout<<"WSAStartup failed!\n";
return 1;
}
//建立套接字
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == INVALID_SOCKET)
{
cout<<"socket() failed; %d\n"<<WSAGetLastError();
WSACleanup();//釋放套接字資源
return 1;
}
//賦值“MyUDP”
ZeroMemory(buf, BUF_SZIE);
strcpy(buf, "MyUDP");
//伺服器地址
servAddr.sin_family =AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons((short)5000);
int nServAddlen = sizeof(servAddr);
//傳送資料
if(sendto(s, buf, BUF_SZIE, 0, (SOCKADDR*)&servAddr, nServAddlen) == SOCKET_ERROR)
{
cout<<"recvfrom() failed: %d\n"<<WSAGetLastError();
closesocket(s); //關閉套接字
WSACleanup(); //釋放套接字資源
return 1;
}
closesocket(s); //關閉套接字
WSACleanup(); //釋放套接字資源
system("pause");
return 0;
}
套接字選項
建立套接字後,通過套接字選項對其各種屬性進行設定,便可對套接字的行為產生影響。
getsockopt()函式用於獲取套接字選項資訊,宣告如下
int getsockopt(SOCKET s,int level,int optname,char FAR* optval,int FAR* optlen);
s: 套接字
level: 選項級別,有SOL_SOCKET和IPPROTO_TCP兩個級別
optname:套接字選項名稱
optval: 接收資料緩衝區,該引數返回套接字選項名稱對應的值。
optlen: 緩衝區大小
setsockopt()函式用於設定套接字選項,宣告如下int setsockopt(SOCKET s,int level,int optname, const char FAR* optval,int FAR* optlen);
s: 套接字
level: 選項級別,有SOL_SOCKET和IPPROTO_TCP兩個級別
optname:套接字選項名稱
optval: 該引數用於設定套接字選項的值。
optlen: 緩衝區大小
SOL_SOCKET選項級別
Socket選項 |
型別 |
預設值 |
說明 |
SO_ACCEPTCON |
BOOL |
FALSE |
套介面正在監聽 |
SO_BROADCAST |
BOOL |
FALSE |
允許套介面傳送廣播資訊。 |
SO_DEBUG |
BOOL |
FALSE |
記錄除錯資訊。 |
SO_DONTLINGER |
BOOL |
FALSE |
不要因為資料未傳送就阻塞關閉操作。 |
SO_DONTROUTE |
BOOL |
FALSE |
禁止選徑;直接傳送。 |
SO_ERROR |
int |
0 |
獲取錯誤狀態並清除。 |
SO_KEEPALIVE |
BOOL |
FALSE |
傳送“保持活動”包。 |
SO_LINGER |
struct linger FAR* |
0 |
關閉時有未傳送資料,則逗留。 |
SO_OOBINLINE |
BOOL |
FALSE |
在常規資料流中接收帶外資料。 |
SO_RCVBUF |
int |
設定或者獲取接收緩衝區的大小。 |
|
SO_REUSEADDR |
BOOL |
FALSE |
允許套介面和一個已在使用中的地址捆綁 |
SO_SNDBUF |
int |
設定或者獲取傳送緩衝區的大小。 |
|
SO_TYPE |
int |
返回套接字型別,如SOCK_DGRAM,SOCK_STREAM。 |
|
SO_SNDTIMEO |
Int |
設定或獲取套接字在傳送資料的超時時間。 |
|
SO_RCVTIMEO |
int |
設定或獲取套接字在接收資料的超時時間。 |
|
TCP_NODELAY |
BOOL |
FALSE |
禁止傳送合併的Nagle演算法 |
詳細用法
1.closesocket(一般不會立即關閉而經歷TIME_WAIT的過程)後想繼續重用該socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
2. 如果要已經處於連線狀態的soket在呼叫closesocket後強制關閉,不經歷TIME_WAIT的過程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
3.在send(),recv()過程中有時由於網路狀況等原因,發收不能預期進行,而設定收發時限:
int nNetTimeout=1000;//1秒
//傳送時限
setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收時限
setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));
4.在send()的時候,返回的是實際傳送出去的位元組(同步)或傳送到socket緩衝區的位元組
(非同步);系統預設的狀態傳送和接收一次為8688位元組(約為8.5K);在實際的過程中傳送資料
和接收資料量比較大,可以設定socket緩衝區,而避免了send(),recv()不斷的迴圈收發:
// 接收緩衝區
int nRecvBuf=32*1024;//設定為32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//傳送緩衝區
int nSendBuf=32*1024;//設定為32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
5. 如果在傳送資料的時,希望不經歷由系統緩衝區到socket緩衝區的拷貝而影響程式的效能:
6.同上在recv()完成上述功能(預設情況是將socket緩衝區的內容拷貝到系統緩衝區):
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));
7.一般在傳送UDP資料報的時候,希望該socket傳送的資料具有廣播特性:
BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));
8.在client連線伺服器過程中,如果處於非阻塞模式下的socket在connect()的過程中可以設定connect()延時,
直到accpet()被呼叫(本函式設定只有在非阻塞的過程中有顯著的作用,在阻塞的函式呼叫中作用不大)
BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));
9.如果在傳送資料的過程中(send()沒有完成,還有資料沒傳送)而呼叫了closesocket(),
以前我們一般採取的措施是"從容關閉"shutdown(s,SD_BOTH),但是資料是肯定丟失了,
如何設定讓程式滿足具體應用的要求(即讓沒發完的資料傳送出去後在關閉socket)
struct linger {
u_short l_onoff;
u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff=1;//(在closesocket()呼叫,但是還有資料沒傳送完畢的時候容許逗留)
// 如果m_sLinger.l_onoff=0;則功能和2.)作用相同;
m_sLinger.l_linger=5;//(容許逗留的時間為5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));