這兩天覆習了很多有關Windows平臺和Linux平臺的Socket編程知識,以前曾經寫過一篇博文,示範過怎麼使用C語言實現TCP鏈接,可是那篇文章寫得很隨意,因此如今我決定要重寫這篇文章,既是爲了總結這兩天學到的知識,也爲了給往後我寫代碼一個參考。linux
下面我用給出兩份C語言代碼來實現TCP鏈接,採用邊寫代碼邊解釋的辦法講解這些程序,越複雜的代碼越難看懂,所以我將給出最簡潔的代碼、包含最少的頭文件來實現一個只發出一句話就隨即關閉的程序。編程
首先總結一下TCP的鏈接過程:小程序
服務端:windows
建立一個Socket 「 socket() 」服務器
建立並初始化本機地址結構體 「 struct sockaddr_in 」網絡
綁定Socket和地址結構體 「 bind() 」多線程
監聽鏈接 「 listen() 」socket
接受鏈接 「 accept() 」函數
開始自由通訊 「 Linux: read()/write() 」 「 Windows: recv()/send() 」spa
關閉Socket 「 close() 」
客戶端:
建立一個Socket 「 socket() 」
建立並初始化服務器地址結構 「 struct sockaddr_in 」
鏈接服務器 「 connect() 」
開始自由通訊 「 Linux: read()/write() 」「 Windows: recv()/send() 」
關閉Socket 「 close() 」
代碼:
服務端(Windows平臺):
#include <stdio.h> #include <winsock2.h> int main(){ //啓動Socket前的初始化 WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); /*在windows平臺上使用Socket編程前 *必須* 先調用WSAStartup()函數初始化一些工做, 是關於ws2_32.dll調用的初始化,ws2_32.dll提供了實現網絡鏈接必要的函數, 當程序運行結束之後須要調用WSACleanup()來清理、釋放資源, 若是以linux爲服務器,則不須要這項工做*/ //建立SOCKET SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /*在一次通訊中,通訊的雙方每邊都會有一個SOCKET,用於標識本身 SOCKET這東西在我看來,就跟一個文件流沒什麼差異, 在linux上咱們能夠調用write()向要通訊的對方的SOCKET中寫入數據, 對方能夠調用read()從他本身的SCOKET中讀取數據咱們剛發送的數據, 就像操做一個普通文件同樣,只不過這個"文件"是儲存在另外一臺電腦上而已, 因此把它理解成一個通訊的通道就行了*/ //綁定本機的地址到SOCKET //首先須要構造一個地址結構體來儲存咱們本身的IP和端口 //這個結構體的具體描述在網上有不少介紹,也能夠打開winsock2.h頭文件查看詳細信息,這裏就很少說了 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; //TCP協議這麼寫就行了 serverAddr.sin_port = htons(1187); //端口號,htons()這個函數你們有必要去了解一下 serverAddr.sin_addr.s_addr = INADDR_ANY; //本機的IP地址,上次我在這裏填了127.0.0.1, //結果局域網裏的其餘機器都鏈接不上,因而改爲了這樣才能夠 //這樣就把本機的IP和SOCKET綁定起來了 bind(serverSocket,(struct sockaddr*)&serverAddr,sizeof(serverAddr)); //設置監聽參數 listen(serverSocket,1); /*第二個參數的意思不是最多能接受多少個鏈接,而是同時能處理幾個主機的鏈接請求, 這裏設置爲1,假設服務器上線之後,同時有兩臺主機請求鏈接, 那麼就最多隻會處理一個鏈接,而另外一臺主機只能鏈接失敗 值得注意的是,程序運行到這裏並不會中止下來,監聽端口,等待鏈接。 我以前一直覺得服務器等待鏈接是在這裏發生的,真正等待鏈接的函數是下面的accept() */ //接受鏈接 struct sockaddr_in clientAddr; int clientAddrSize = sizeof(struct sockaddr_in); SOCKET clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrSize); /*這只是個簡單示例,因此只用了一個客戶SOCKET,也就是說只要有一臺主機鏈接進來, 咱們就不會再處理新的鏈接,而真實的服務器通常都是用多線程來接受客戶鏈接的, 每接收到一個新的鏈接,就建立一個新線程來跟客戶端交互。 程序運行到accept()函數這裏就會阻塞住,直到有新的鏈接纔會繼續運行下去, accpet()函數須要一個sockaddr_in結構圖做爲參數, 函數調用完畢後這個結構體裏就儲存着客戶端的IP地址, accpet()函數返回的是新鏈接的SOCKET,咱們下面跟對方交互就用這個SOCKET。*/ //向客戶端發送數據 char str[] = "Hello,friend!"; send(clientSocket, str, sizeof(str), 0); /*發送數據很簡單,參數基本都能理解,最後一個參數我尚未仔細研究過,只知道通常置0就行了*/ //關閉套接字 closesocket(serverSocket); closesocket(clientSocket); //首尾呼應 WSACleanup(); return 0; }
客戶端(Linux平臺):
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> int main(){ //構造socket int mySocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); printf("Socket: %d\n",mySocket); //鏈接服務器,用的是connect()函數,調用須要一個sockaddr_in結構體 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(1187); serverAddr.sin_addr.s_addr = inet_addr("192.168.0.XXX"); //這裏填上服務器的IP地址 connect(mySocket, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr)); //接收服務器發送的數據 char buffer[40]; read(mySocket, buffer, sizeof(buffer)-1); printf("Message form server: %s\n", buffer); //關閉鏈接 close(mySocket); return 0; }
這只是一個簡單的、不完整的、有BUG的小程序,還有不少沒有作到位的地方,望高手指點。