C語言實現TCP鏈接

這兩天覆習了很多有關Windows平臺和Linux平臺的Socket編程知識,以前曾經寫過一篇博文,示範過怎麼使用C語言實現TCP鏈接,可是那篇文章寫得很隨意,因此如今我決定要重寫這篇文章,既是爲了總結這兩天學到的知識,也爲了給往後我寫代碼一個參考。linux

下面我用給出兩份C語言代碼來實現TCP鏈接,採用邊寫代碼邊解釋的辦法講解這些程序,越複雜的代碼越難看懂,所以我將給出最簡潔的代碼、包含最少的頭文件來實現一個只發出一句話就隨即關閉的程序。編程

 

首先總結一下TCP的鏈接過程:小程序

服務端:windows

  1. 建立一個Socket    「 socket() 」服務器

  2. 建立並初始化本機地址結構體    「 struct sockaddr_in 」網絡

  3. 綁定Socket和地址結構體     「 bind() 」多線程

  4. 監聽鏈接     「 listen() 」socket

  5. 接受鏈接     「 accept() 」函數

  6. 開始自由通訊     「 Linux: read()/write() 」 「 Windows: recv()/send() 」spa

  7. 關閉Socket      「 close() 」

     

客戶端:

  1. 建立一個Socket     「 socket() 」

  2. 建立並初始化服務器地址結構    「 struct sockaddr_in 」

  3. 鏈接服務器      「 connect() 」

  4. 開始自由通訊        「 Linux: read()/write() 」「 Windows: recv()/send() 」

  5. 關閉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的小程序,還有不少沒有作到位的地方,望高手指點。

相關文章
相關標籤/搜索