JK轉至:https://www.cnblogs.com/hgwang/p/6074038.htmlhtml
流程圖:node
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")
WSAStartup 函數用於初始化供進程調用的Winsock相關的dll。編程
1 int WSAStartup( 2 __in WORD wVersionRequested, 3 __out LPWSADATA lpWSAData 4 );
標識了用戶調用的Winsock的版本號。高字節指明輔版本編號,低字節指明主版本編號。一般使用MAKEWORD來生成一個版本號。 當前Winsock sockets的版本號爲2.2,用到的dll是 Ws2_32.dll。
服務器
指向WSADATA結構體的指針,lpWSAData返回了系統對Windows Sockets 的描述。網絡
若是調用成功,WSAStartup 函數返回0。不然,將返回五種錯誤代碼之一。但絕對不能使用WSAGetLastError獲取錯誤代碼。多線程
WSAData wsa; if (::WSAStartup(MAKEWORD(2,2),&wsa) != 0) { cout<<"WSAStartup error"<<endl; return 0; }
該函數釋放對Winsock連接庫的調用。less
int WSACleanup(void);
返回值0表示正常退出,返回值SOCKET_ERROR表示異常。返回值是SOCKET_ERROR,能夠調用 WSAGetLastError.查看錯誤代碼。須要注意的是,在多線程環境下,WSACleanup 函數將終止全部線程的socket操做。socket
socket函數將建立指定傳輸服務的socket。ide
1 SOCKET WSAAPI socket( 2 __in int af, 3 __in int type, 4 __in int protocol 5 );
af ( address family)函數
指明地址簇類型,經常使用的地址簇以下,其他地址簇在Winsock2.h中定義。
AF_UNSPEC(未指明)、
AF_INET(IPv4)、
AF_NETBIOS(NETBIOS地址簇)、
AF_INET6(IPv6)、
AF_IRDA(Infrared Data Association (IrDA)地址簇)、
AF_BTM(Bluetooth)。
type
指明socket的類型,Windows Sockets 2常見類型以下:
SOCK_STREAM(流套接字,使用TCP協議)、
SOCK_DGRAM(數據報套接字,使用UDP協議)、
SOCK_RAW(原始套接字)、
SOCK_RDM(提供可靠的消息數據報文,reliable message datagram)、
SOCK_SEQPACKET(Provides a pseudo-stream packet based on datagrams,在UDP的基礎上提供了僞流數據包)。
protocol
指明數據傳輸協議,該參數取決於af和type參數的類型。protocol參數在Winsock2.h and Wsrm.h定義。一般使用以下3中協議:
IPPROTO_TCP(TCP協議,使用條件,af是AF_INET or AF_INET六、type是SOCK_STREAM )
IPPROTO_UDP(UDP協議,使用條件,af是AF_INET or AF_INET六、type是SOCK_DGRAM)
IPPROTO_RM(PGM(Pragmatic General Multicast,實際通用組播協議)協議,使用條件,af是AF_INET 、type是SOCK_RDM)
If no error occurs, socket returns a descriptor referencing the new socket. Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.
若是不出錯,socket函數將返回socket的描述符(句柄),不然,將返回INVALID_SOCKET。
1 SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 2 if (s == INVALID_SOCKET) 3 { 4 int er = WSAGetLastError(); 5 return 0; 6 }
bind函數將socket關聯一個本地地址。
1 int bind( 2 __in SOCKET s, 3 __in const struct sockaddr* name, 4 __in int namelen 5 );
s
指定一個未綁定的socket。
name
指向sockaddr地址的指針,該結構含有IP和PORT
namelen
參數name的字節數。
If no error occurs, bind returns zero. Otherwise, it returns SOCKET_ERROR, and a specific error code can be retrieved by calling WSAGetLastError.
無錯誤返回0,又錯誤返回SOCKET_ERROR。
1 sockaddr_in service; 2 service.sin_family = AF_INET; 3 service.sin_addr.s_addr = inet_addr("127.0.0.1"); 4 service.sin_port = htons(27015); 5 6 //---------------------- 7 // Bind the socket. 8 if (bind( ListenSocket, (SOCKADDR*) &service,sizeof(service)) == SOCKET_ERROR) { 9 closesocket(ListenSocket); 10 return; 11 }
sin_port和sin_addr都必須是網絡字節序(NBO),通常可視化的數字都是主機字節序(HBO)。
兩者長度同樣,都是16個字節,即佔用的內存大小是一致的,所以能夠互相轉化。兩者是並列結構,指向sockaddr_in結構的指針也能夠指向sockaddr。
sockaddr經常使用於bind、connect、recvfrom、sendto等函數的參數,指明地址信息,是一種通用的套接字地址。
sockaddr_in 是internet環境下套接字的地址形式。因此在網絡編程中咱們會對sockaddr_in結構體進行操做,使用sockaddr_in來創建所需的信息,最後使用類型轉化就能夠了。通常先把sockaddr_in變量賦值後,強制類型轉換後傳入用sockaddr作參數的函數:sockaddr_in用於socket定義和賦值;sockaddr用於函數參數。
題外話,兩個函數 htons() 和 inet_addr()。
htons()做用是將端口號由主機字節序轉換爲網絡字節序的整數值。(host to net)
各個機器cpu對數據存儲和表示的方法不通,intel機器用littele-endian存數據,而IBM機器用big-endian存數據。網絡協議爲取消這種差別,一致採用big-endian方式。htons用於將unsigned short的數值從littele-endian轉換爲big-endian,因爲short只有2字節,經常使用語port數值的轉換。htons用於將unsigned long的數值從littele-endian轉換爲big-endian,long有4字節,經常使用於ipv4地址的轉換。
inet_addr()做用是將一個IP字符串轉化爲一個網絡字節序的整數值,用於sockaddr_in.sin_addr.s_addr。
inet_ntoa()做用是將一個sin_addr結構體輸出成IP字符串(network to ascii)。
printf("%s",inet_ntoa(mysock.sin_addr));
htonl()做用和htons()同樣,不過它針對的是32位的(long),而htons()針對的是兩個字節,16位的(short)。
與htonl()和htons()做用相反的兩個函數是:ntohl()和ntohs()。
INADDR_ANY
數值爲0。不少帖子對這個值的理解不一。我查了一下CMU(卡耐基梅隆大學cs課程,深刻理解計算機系統做者是該校計算機學院院長)的資料,看到以下解釋:
源自:https://www.cs.cmu.edu/~srini/15-441/F01.full/www/assignments/P2/htmlsim_split/node18.html
用INADDR_ANY來配置IP地址,意味着不須要知道當前服務器的IP地址。對於多網卡的服務器,INADDR_ANY容許你的服務接收一個服務器上全部網卡發來的數據。下面例子也有寫,若是某個socket使用INADDR_ANY和8000端口,那麼它將接收該全部網卡傳來的數據。而其餘socket將沒法再使用8000端口。
1 sockaddr_in service; 2 ZeroMemory((char *)&service,sizeof(sockaddr_in)); 3 service.sin_family = AF_INET; 4 //service.sin_addr.S_un.S_addr =/*INADDR_ANY*/ inet_addr("127.0.0.1"); 5 service.sin_addr.s_addr = INADDR_ANY; 6 service.sin_port = htons(8278); 7 if (bind(s,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR) 8 { 9 int er = WSAGetLastError(); 10 closesocket(s); 11
description:The listen function places a socket in a state in which it is listening for an incoming connection
int listen( __in SOCKET s, __in int backlog );
1 if (listen(s,SOMAXCONN ) == SOCKET_ERROR) 2 { 3 int er = WSAGetLastError(); 4 closesocket(s); 5 }
description:The accept function permits an incoming connection attempt on a socket.
1 SOCKET accept( 2 __in SOCKET s, 3 __out struct sockaddr* addr, 4 __in_out int* addrlen 5 );
A descriptor that identifies a socket that has been placed in a listening state with the listen function. The connection is actually made with the socket that is returned by accept.
listen函數用到的socket。accept函數將建立鏈接。
An optional pointer to a buffer that receives the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family that was established when the socket from the sockaddr structure was created.
指向通訊層鏈接實體地址的指針。addr 的格式取決於bind函數內地址簇的類型。
An optional pointer to an integer that contains the length of structure pointed to by the addr parameter.
addr的長度。
If no error occurs, accept returns a value of type SOCKET that is a descriptor for the new socket. This returned value is a handle for the socket on which the actual connection is made.
Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.
The integer referred to by addrlen initially contains the amount of space pointed to by addr. On return it will contain the actual length in bytes of the address returned.
若是不發生錯誤,accept將返回一個新的SOCKET描述符,即新建鏈接的socket句柄。不然,將返回INVALID_SOCKET。傳進去的addrlen應該是參數addr的長度,返回的addrlen是實際長度。
1 SOCKET ac = accept(s,NULL,NULL); 2 if (ac == INVALID_SOCKET) 3 { 4 int er = WSAGetLastError(); 5 closesocket(s); 6 } 複製代碼
accept: https://blog.csdn.net/gqingmo/article/details/52231866
Descriptor identifying an unconnected socket.
Name of the socket in the sockaddr structure to which the connection should be established.
與bind函數的name參數相似,指明待鏈接的地址
Length of name, in bytes.
If no error occurs, connect returns zero. Otherwise, it returns SOCKET_ERROR, and a specific error code can be retrieved by calling WSAGetLastError.
On a blocking socket, the return value indicates success or failure of the connection attempt.
0表示正確,不然,將返回SOCKET_ERROR。若是是阻塞式的socket鏈接,返回值表明了鏈接正常與失敗。
1 sockaddr_in server; 2 server.sin_family = AF_INET; 3 server.sin_port = htons(8828); 4 server.sin_addr.s_addr = inet_addr("127.0.0.1"); 5 if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR) 6 { 7 break; 8 }
函數原型: int send(SOCKET S, const char *buf , int len, int flags)
例子:
char buf[1024] = {"abcdefghijklmnopqrstuvwxyz"};
int sent = 0;//已發送
int total_size = sizeof(buff);
sent = send(sock, buff+sent, total_size - sent, 0);
返回值:
(1),無錯:返回發送字節數, 可能小於len;
(2)、出錯:返回SOCKET_ERROR,可經過WSAGetLastError獲取錯誤碼
int send( __in SOCKET s, __in const char* buf, __in int len, __in int flags );
int recv(
__in SOCKET s,
__out char* buf,
__in int len,
__in int flags
);
If no error occurs, send returns the total number of bytes sent, which can be less than the number requested to be sent in the len parameter. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
send的返回值標識已發送數據的長度,這個值可能比參數len小,這也意味着數據緩衝區沒有所有發出去,要進行後續處理。返回SOCKET_ERROR標識send出錯。
If no error occurs, recv returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
recv的返回值標識已接收數據的長度。若是鏈接已關閉,返回值將是0。返回SOCKET_ERROR標識recv出錯。
若是接收緩衝區沒有數據可接收,則recv將阻塞直至有數據到達。