socket網絡編程基礎

IP地址結果

在winsock中,APP通過SOCKADDR_IN結構來指定IP和Port信息,
其中的sin_zero只充當填充項,以使SOCKADDR_IN結構和SOCKADDR結構的長度一樣。
unsigned long inet_addr(const char FAR* cp)用於將一個點分IP轉換成一個32位無符號長整數(按網絡字節順序)。

字節順序

在computer中把IP & Port指定成多字節數時,是按主機字節(host-byte)順序進行的,
但在網絡上指定IP & Port,標準指定必須使用big-endian即網絡字節(network-byte)順序表示。

主機字節順序轉成網絡字節順序:

u_long WSAAPI htonl( _In_ u_long hostlong);
int WSAAPI WSAHtonl(_In_  SOCKET s,
  _In_  u_long hostlong,
  _Out_ u_long *lpnetlong
);
u_short WSAAPI htons(_In_ u_short hostshort);
int WSAAPI WSAHtons(_In_  SOCKET  s,
  _In_  u_short hostshort,
  _Out_ u_short *lpnetshort
);

網絡字節順序轉主機字節順序:

u_long WSAAPI ntohl(_In_ u_long netlong);
int WSAAPI WSANtohl(_In_  SOCKET s,
  _In_  u_long netlong,
  _Out_ u_long *lphostlong
);
u_short WSAAPI ntohs(_In_ u_short netshort);
int WSAAPI WSANtohs(_In_  SOCKET  s,
  _In_  u_short netshort,
  _Out_ u_short *lphostshort
);

IP地址和主機名

IP地址不便於記憶,通常都使用主機名,使用如下這些地址&名稱解析函數,
可以將主機名(如www.somewebsite.com)解析爲IP地址、服務名稱(如FTP)和端口號:
getaddrinfo,
getnameinfo,
gethostbyaddr,
gethostbyname,
gethostname,
getprotobyname,
getprotobynumber,
getservbyname,
getservbyport等,
同時還有這些函數對應的異步版本,如:
WSAAsyncGetHostByAddr,
WSAAsyncGetHostByName,
WSAAsyncGetProtoByName,
WSAAsyncGetProtoByNumber,
WSAAsyncGetServByName,
WSAAsyncGetServByPort等

創建socket

有兩個函數可以創建套接字:

SOCKET WSAAPI socket(
  _In_ int af,//IPv4爲AF_INET
  _In_ int type,//tcp爲SOCK_STREAM,udp爲SOCK_DGRAM
  _In_ int protocol//tcp爲IPPROTO_TCP,udp爲IPPROTO_UDP
);
SOCKET WSASocket(
  _In_ int                af,
  _In_ int                type,
  _In_ int                protocol,
  _In_ LPWSAPROTOCOL_INFO lpProtocolInfo,
  _In_ GROUP              g,
  _In_ DWORD              dwFlags
);
//爲控制套接字的選項和行爲,提供了4個API:
setsockopt
getsockopt
ioctlsocket
WSAIoctl

綁定

int bind(
  _In_ SOCKET                s,
  _In_ const struct sockaddr *name,
  _In_ int                   namelen
);
//最常見的錯誤是WSAEADDRINUSE:如果使用的是tcp/ip,那麼表示該IP&Port已經被佔用了,或者該IP&Port處於TIME_WAIT狀態
//對一個已經bind的套接字調用bind,將返回WSAEFAULT錯誤

監聽

int listen(
  _In_ SOCKET s,
  _In_ int    backlog//隊列長度,超出隊列時請求將被拒絕,連接段收到WSAECONNREFUSED錯誤
);
//最常見的錯誤是WSAEINVAL,表示在調用listen之前沒有調用bind
//另外listen也可能接受到WSAEADDRINUSE錯誤,該錯誤通常發生在bind調用

接受連接

接受連接的API:accept, WSAAccept, AcceptEx.

SOCKET accept(
  _In_    SOCKET          s,//bindsocket
  _Out_   struct sockaddr *addr,
  _Inout_ int             *addrlen
);
//當監聽套接字爲異步或者非阻塞模式,並且沒有連接被接受時,最常見的錯誤是WSAEWOULDBLOCK

服務器端過程

//1.初始化winsock
WSAStartup(MAKEWORD(2,2), &wsaData);
//2.創建監聽套接字
lstSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//3.建立SOCKADDR_IN地址結構
SOCKADDR_IN srvAddr;
srvAddr.sin_faminly = AF_INET;
srvAddr.sin_port = htons(6188);
//4.綁定
bind(lstSocket, (SOCKADDR*)&srvAddr, sizeof(srvAddr));
//5.監聽客戶端連接
listen(lstSocket, 5);
//6.接受新連接(通常循環接受多個連接)
clientSocket = accept(lstSocket, (SOCKADDR*)&clientAddr, &clientAddrLen);
//7.在clientSocket上收發數據
//8.使用完關閉clientSocket
closesocket(clientSocket);
//9.關閉lstSocket
closesocket(lstSocket);
//10.釋放
WSACleanup();

客戶端過程

1)創建socket
2)建立SOCKADDR地址結構,指定服務器IP&Port
3)調用connect, WSAConnect或者ConnectEx建立客戶端和服務器的連接

int connect(
  _In_ SOCKET                s,
  _In_ const struct sockaddr *name,
  _In_ int                   namelen
);
//常見錯誤

客戶端的完整過程如下:

//1.初始化winsock
WSAStartup(MAKEWORD(2,2), &wsaData);
//2.創建客戶端套接字
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//3.建立SOCKADDR_IN地址結構,用於連接服務器
SOCKADDR_IN srvAddr;
srvAddr.sin_faminly = AF_INET;
srvAddr.sin_port = htons(6188);
srvAddr.sin_addr.s_addr = inet_addr("136.149.3.28");
//4.用套接字創建一個到服務器的連接
connect(s, (SOCKADDR*)&srvAddr, sizeof(srvAddr));
//5.使用套接字s收發數據
//6.使用完後,關閉套接字
closesocket(s);
//7.清理
WSACleanup();

TCP狀態

1)對於每個套接字而言,它的初始狀態都是CLOSED.

2)若服務器套接字同本地IP&Port綁定起來,並在它上面進行監聽,那麼套接字的狀態就是LISTEN狀態.

3)若客戶機初始化了一個連接,就會向服務器發送一個SYN包,同時將客戶機套接字狀態置爲SYN_SENT.

4)服務器收到SYN包後,會發送一個SYN_ACK包響應(如果服務器一直不發送SYN_ACK包,客戶機就會超時,並返回CLOSED狀體),服務器的套接字狀態變爲SYN_RCVD;

5)客戶機需要用一個ACK包對它(SYN_ACK包)進行響應,此時客戶機的套接字將處於ESTABLISHED狀態;這個ACK包將服務器套接字的狀態變成ESTABLISHED.

6) 14…

數據傳輸

所有收發數據的緩衝區都屬於簡單的char類型(面向字節的數據),它可以包含任何原始數據,這些原始數據是二進制,還是字符型,是無關緊要的。
發送:send/WSASend
接收:recv/WSARecv
它們出錯返回值都是SOCKET_ERROR,最常見的錯誤是WSAECONNABORTED和WSAECONNRESET,兩者都和正在被關閉相關(要麼由於超時被關閉,要麼由於通信方正在關閉連接). 另一個常見的錯誤是WSAEWOULDBLOCK,一般出現在非阻塞模式或異步狀態時,表示函數暫時不能完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章