當我在單機世界摸爬滾打數月時,我也跟著時代的潮流,正式的跨入了網路化編程時代。學習一項新技術,永遠是一件令人興奮的事情。但是,想要找到一個好的學習教程無疑是一件困難的事情,搜尋百度Google無數次,終於踏入了Winsock的大門,我會在學習Windows網路編程中的心得體會發布出來,供大家學習。因為我也是新手,如果在博文當中有什麼錯誤的地方,還請各位高手大大提出來,以協助本人改正錯誤。
如果你原來學習過Winsock編程,那麼看本篇文章將會非常輕鬆。如果沒學過,也沒關係,本篇博文將會詳細講解。OK,廢話不多說,直接跳到主題。
1.在我們使用任何winsock函數之前,必須要現在標頭檔中添加winsock2.h和ws2_32.lib檔案。
#include <winsock2.h>#pragma comment(lib, "ws2_32.lib")
同時,我們要注意:winsock2.h標頭檔必須要放在windows.h標頭檔的上面,要不然編譯的時候會出錯。
2.在我們調用任何winsock函數之前,還要必須通過WSAStartup函數完成對winsock服務的初始化。
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData )
(1)wVersionRequested:一個WORD(雙位元組)型數值,在最高版本的Windows Sockets支援調用者使用,高階位元組指定小版本(修訂本)號,低位位元組指定主要版本號。一般使用MAKEWORD(2,2)
(2)lpWSAData 指向WSADATA資料結構的指標,用來接收Windows Sockets實現的細節。
3.當我們調用了WSAStartup函數後,便可以調用其他的winsock函數了。使用WSASocket函數建立一個與指定服務提供者捆綁的通訊端。
WSASocket(int af, int type, int protocol, LPWSAPROTOCOL_INFOW lpProtocolInfo, GROUP g, DWORD dwFlags);
(1)af:一個地址族規範,我們使用AF_INET。
(2)type:新通訊端的類型描述,我們使用SOCK_STREAM,也就是使用TCP
(3)protocol:通訊端使用的特定協議,如果調用者不願指定協議則定為0
(4)lpProtocolInfo:一個指向PROTOCOL_INFO結構的指標,該結構定義所建立套介面的特性。如果本參數非零,則前三個參數(af, type, protocol)被忽略。
(5)g:保留給未來使用的通訊端組。套介面組的標識符。
(6)dwFlags:套介面屬性描述。
一般我們都是用前面三個,後面的都為NULL。如果成功,返回的是一個SOCKET類型的通訊端。如果返回失敗,則返回SOCKET_ERROR
4.我們使用connect函數串連上我們指定的網站。
connect(SOCKET s, const struct sockaddr FAR * name, int namelen);
(1)s:通訊端描述符,也就是我們調用的WSASocket函數返回的通訊端
(2)name:一個SOCKADDR類型,不過我們一般使用SOCKADDR_IN類型,也就是是我們需要串連的IP,連接埠等等
(3)namelen:SOCKADDR結構體的長度。
SOCKADDR_IN ipconfig;HOSTENT * iphost;char * url = "baidu.com";if((iphost = gethostbyname(url)) != NULL) ipconfig.sin_addr.S_un.S_addr = inet_addr(inet_ntoa(*((struct in_addr *)iphost->h_addr_list[0])))ipconfig.sin_port = htons(80);ipconfig.sin_family = AF_INET;
可以看我的另外一篇博文:使用C語言擷取指定網域名稱的IP
5.當我們串連以後,就需要發送http協議頭資訊了,如下:
char * httpHeader = "GET /index.html\r\nAccept: application/javascript, */*;q=0.8\r\nHost: baidu.com\r\nConnection: Keep-Alive\r\n\r\n"
然後使用send函數將其發送給主機。
send(sock, httpHeader, strlen(httpHeader) + 1, 0)
6.如果我們發送出去以後,需要使用recv函數進行接收從伺服器返回過來的資訊,其中,第一次返回的是伺服器返回過來的HTTP頭,我們可以從中看見是否成功,其後返回過來的則是網頁的原始碼:
int i = 0, check, err = 0;char recvChar[100];char recvCDa[500];if(recv(sock, recvCDa, 500, 0) == SOCKET_ERROR) { fprintf(stderr, "返回資訊失敗,錯誤碼:%d",WSAGetLastError()); system("pause"); return 1; } printf("伺服器返回HTTP頭:%s\n", recvCDa); memset(recvChar, '\0', 101); while((check = recv(sock, recvChar, sizeof(recvChar), 0)) != SOCKET_ERROR) { if(check < 100) err = 1; printf("%s", recvChar); memset(recvChar, 0, 100); if(err == 1) break; i++; }
OK,下面附上一段整體的擷取百度原始碼的代碼:
#include <stdio.h>#include <string.h>#include <winsock2.h>#include <Windows.h>#pragma comment(lib, "ws2_32.lib")using namespace std;char * urlIp(char * url);int main(int argc, char * argv[]){ SOCKADDR_IN url_ipconfig = {sizeof(SOCKADDR_IN)}; char * sendChar; WSADATA wsaDa; SOCKET sock; char ip[20]; char sendString[100]; char recvChar[100]; char recvCDa[500]; if(WSAStartup(MAKEWORD(2,2), &wsaDa) == SOCKET_ERROR) { fprintf(stderr, "初始化WSAStartup出錯,錯誤碼:%d",WSAGetLastError()); system("pause"); return 1; } if((sock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, NULL, 0)) == SOCKET_ERROR) { fprintf(stderr, "初始化WSAStartup出錯,錯誤碼:%d",WSAGetLastError()); system("pause"); return 1; } strcpy(ip, urlIp("baidu.com")); url_ipconfig.sin_family = AF_INET; url_ipconfig.sin_port = htons(80); url_ipconfig.sin_addr.S_un.S_addr = inet_addr(ip); if(connect(sock, (SOCKADDR *)&url_ipconfig, sizeof(url_ipconfig)) == SOCKET_ERROR) { fprintf(stderr, "串連伺服器失敗,錯誤碼:%d",WSAGetLastError()); system("pause"); return 1; } strcpy(sendString, "GET /index.html HTTP/1.1\r\nAccept: application/javascript, */*;q=0.8\r\nHost: baidu.com\r\nConnection: Keep-Alive\r\n\r\n"); if(send(sock, sendString, strlen(sendString) + 1, 0) == SOCKET_ERROR) { fprintf(stderr, "發送GET包失敗失敗,錯誤碼:%d\n",WSAGetLastError()); system("pause"); return 1; } memset(recvCDa, '\0', 500); if(recv(sock, recvCDa, 500, 0) == SOCKET_ERROR) { fprintf(stderr, "返回資訊失敗,錯誤碼:%d",WSAGetLastError()); system("pause"); return 1; } printf("%s\n", recvCDa); int i = 0, check, err = 0; memset(recvChar, '\0', 101); while((check = recv(sock, recvChar, sizeof(recvChar), 0)) != SOCKET_ERROR) { if(check < 100) err = 1; printf("%s", recvChar); memset(recvChar, 0, 100); if(err == 1) break; i++; } system("pause"); return 0;}char * urlIp(char * url){ HOSTENT * iphost; char ipconfig[20]; char ip[20]; if((iphost = gethostbyname(url)) != NULL) { memcpy(&ip, inet_ntoa(*((struct in_addr *)iphost->h_addr_list[0])), 20); } return ip;}