Winsock主要有以下幾個主要的頭文件和庫文件用於應用程序調用Winsock的API:
Winsock.h WSOCK32.lib
Winsock2.h WS2_32.lib
MSWSOCK.h Mswsock.lib
其它的一些常用的TCP/IP協議支持擴展API的頭文件:
WS2tcpip.h
MSTcpip.h
Iphlpapi.h Iphlpapi.lib
同時包含Winsock2.h和Windows.h時注意在Windows.h前定義#define WIN32_LEAN_AND_MEAN 宏,或者可以在Windows.h前包含Winsock2.h或只包含Winsock2.h頭文件
在Windows上使用socket需要初始化環境:
int WSAStartup(
__in WORD wVersionRequested,
__out LPWSADATA lpWSAData
);
wwVersionRequested有兩個字節,用來指定需要初始化的Winsock版本號;一般初始化2.2版本
e.g. {wwVersionRequested = MAKEWORD(2, 2)}
typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1]; unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR* lpVendorInfo;
} WSADATA, *LPWSADATA;
wVersion是初始化成功的Winsock版本
wHighVersion是當前系統中最高的Winsock版本
(以上兩字段中高字節是副版本號,低字節是主版本號)
該結構中其餘字段都可以忽略不用(有其它方法可以得到比這些字段更準確的信息,如調用WSAEnumProtocols)
在調用完畢Winsock的功能後,最終應調用WSAClearup來釋放Winsock庫
int WSACleanup(void);
該函數還可以釋放所有調用Winsock過程中佔用的資源,並釋放所有掛起的Winsock調用(異步調用中產生的)
當然如果不調用這個函數,最終應用程序退出時系統會自動清理這些資源並釋放掛起的調用
但是不要依賴最終程序的退出進行釋放,而總是主動的調用WSAClearup
SOCKET socket(int af, int type, int protocol)
int bind(SOCKET s,const struct sockaddr FAR *name, int namelen) //綁定套接字到指定的地址上
int listen(SOCKET s, int backlog) //將socket設置為監聽模式
第二個參數指定了待處理的併發連接的最大長度
int connect(SOCKET s, const struct sockaddr FAR *name, int namelen)
SOCKET accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)
第二、三個參數用於返回連接進來的客戶端的地址,在IP協議中這就是一個IP地址,需要強轉為SOCKADDR_IN*類型 而返回值就表示與連接進入的客戶端進行通訊操作的新的SOCKET的句柄
int send(SOCKET s,const char* buf,int len,int flags);
int recv(SOCKET s,char* buf,int len,int flags);
int sendto(SOCKET s,const char* buf,int len,int flags,const struct sockaddr* to,int tolen);
int recvfrom(SOCKET s,char* buf,int len,int flags,struct sockaddr* from,int* fromlen);
一方調用send發送時,另一方應該是調用recv接收數據,反之依然
如果兩端同時調用send或recv函數那麼稍後一端的調用會失敗
int shutdown(SOCKET s, int how)
int closesocket(SOCKET s)
Windows下通過SOCKADDR_IN這個結構體來指定IP地址和端口信息
IP地址一般是用"Internet標準點分表示法"來表示的,形如a.b.c.d
其中每個字母代表一個字節數字(可以是十進制/十六進制/八進制表示,注意大小不超過256)
利用inet_addr工具函數,可以方便的將一個點分法表示的IP地址翻譯為一個4字節的無符號整數:
unsigned long inet_addr(__in const char* cp);
注意這個函數參數為MBCS/ANSI string,沒有UNICODE版,這個函數來源於古老的BSD Socket規範,那時候還沒有UNICODE的說法
對於一個具體的網絡協議來說,字節序是一個重要的特徵
Internet協議(TCP/IP協議)的字節序是big-endian
而Intelx86系列主機的字節序卻是little-endian
這樣就存在一個主機字節序轉換為網絡字節序的問題
有一組函數用於將主機字節序轉為網絡字節序:
u_long htonl(__in u_long hostlong);
int WSAHtonl(__in SOCKET s,__in u_long hostlong,
__out u_long* lpnetlong);
u_short htons(__in u_short hostshort);
int WSAHtons(__in SOCKET s,__in u_short hostshort,
__out u_short* lpnetshort);
下一組函數執行相反的操作:
u_long ntohl(_in u_long netlong);
int WSANtohl(__in SOCKET s,__in u_long netlong,
__out u_long* lphostlong);
u_short ntohs(__in u_short netshort);
int WSANtohs(__in SOCKET s,__in u_short netshort,
__out u_short* lphostshort);
(以上函數的現代實現版本是很高效的)
int getpeername(SOCKET s,struct sockaddr FAR* name,int FAR* namelen);
該函數用於在面向連接通訊時取得通訊另一端的IP地址和端口信息
int WSADuplicateSocket(SOCKET s,DWORD dwProcessId,LPWSAPROTOCOL_INFO lpProtocolInfo);
該函數用於跨進程複製SOCKET句柄,與一般的複製句柄方法不同,該函數返回用於複製的SOCKET句柄的WSAPROTOCOL_INFO結構信息,另一進程得到這個信息後調用WSASocket方法傳遞該結構再創建一個新的SOCKET句柄
//////////////////////////////////////////////////////////////////
//可以取得本地IP
hostent* thisHost = gethostbyname("");
char *ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);
SOCKADDR_IN.sin_addr.s_addr = inet_addr(ip);
//////////////////////////////////////////////////////////////////
getsockname 可以獲取指定socke的地址
相對於抽象的網絡協議,而程序員更關心的是如何對網絡進行編程
這需要對抽象的協議的具體化方面的認知以及對一些協議共有特徵方面的知識
具體的網絡編程目前在各個平臺上主要通過套接字編程模型來實現
基本的套接字模型甚至主要的函數方法在各個平臺上(Windows/UNIX/Linux等)幾乎是一樣的,只是各自擴展部分或高級部分不相同
套接字編程模型是與具體的協議無關的網絡編程接口
套接字只關注網絡協議的抽象特徵部分,而不是關係體局是什麼協議,比如:關係是否面向連接/地址如何表達等
所以,在進行網路編程的時候,需要考慮的是網絡寫的具有的一些特徵:
- 面向消息 - 偽流
- 面向連接和無連接
- 可靠性和次序性
- 從容關閉
- 廣播數居
- 多播數據
- 服務質量
- 部分消息
- 路由選擇的考慮
- 字節序
- 最大傳輸單元
- 其他
閱讀更多 攻防基地 的文章