1. 程式人生 > >C/C++ socket套接字詳解(Windows)

C/C++ socket套接字詳解(Windows)

一、編譯環境

本篇部落格是在windows系統下的CodeBlocks環境下編寫而成的,Linux系統以及其他編譯環境暫不適用

關於如何CodeBlocks如何安裝和配置/,可以參考連結(轉自螢火蟲塔莉):CodeBlocks的安裝以及編譯器的配置

常見問題:

編譯時不能識別socket,需要手動匯入lib庫。具體方法連結:https://blog.csdn.net/buaa1214wwj/article/details/52033868

如果導這兩個lib庫仍然不能識別,出現以下情況,重新建立C++工程。


二、socket用法簡介

(1)socket概念

  • socket翻譯為套接字,它是計算機之間通訊的一種約定或一種方式,通過socket這種約定,一臺計算機可以接受其他計算機的資料,也可以向其他計算機發送資料。
  • 學習socket,也就是學習計算機如何通訊,並且編寫出實用的程式。

(2)Windows程式示例

伺服器演示程式碼Server.cpp

#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")  //載入 ws2_32.dll


int main(){
    //初始化 DLL
    WSADATA wsaData;
    WSAStartup( MAKEWORD(2, 2), &wsaData);


    //建立套接字
    SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);


    //繫結套接字
    sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));  //每個位元組都用0填充
    sockAddr.sin_family = PF_INET;  //使用IPv4地址
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具體的IP地址
    sockAddr.sin_port = htons(1234);  //埠
    bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));


    //進入監聽狀態
    listen(servSock, 20);


    //接收客戶端請求
    SOCKADDR clntAddr;
    int nSize = sizeof(SOCKADDR);
    SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);


    //向客戶端傳送資料
    char *str = "Hello World!";
    send(clntSock, str, strlen(str)+sizeof(char), NULL);


    //關閉套接字
    closesocket(clntSock);
    closesocket(servSock);


    //終止 DLL 的使用
    WSACleanup();


    return 0;
}

客戶端演示程式碼Client.cpp

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")  //載入 ws2_32.dll
int main(){
    //初始化DLL
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    //建立套接字
    SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    //向伺服器發起請求
    sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));  //每個位元組都用0填充
    sockAddr.sin_family = PF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockAddr.sin_port = htons(1234);
    connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
    //接收伺服器傳回的資料
    char szBuffer[MAXBYTE] = {0};
    recv(sock, szBuffer, MAXBYTE, NULL);
    //輸出接收到的資料
    printf("Message form server: %s\n", szBuffer);
    //關閉套接字
    closesocket(sock);
    //終止使用 DLL
    WSACleanup();
    system("pause");
    return 0;
}

(3)程式解釋

1. #pragma comment (lib, "ws2_32.lib") :Windows下的socket程式依賴Winsock.dll或者ws2_32.dll,所以必須提前載入
2. WSAStartup()函式對dll初始化,以指明Winsock的版本號,原型:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

    1)wVersionRequested 為 WinSock 規範的版本號,一般採用2.2版本,型別為 WORD,等價於 unsigned short,是一個整數,所以需要用 MAKEWORD() 巨集函式對版本號進行轉換。
    2)lpWSAData 為指向 WSAData 結構體的指標。

3.socket() 函式建立套接字,原型:(Windows 不把套接字作為普通檔案對待,而是返回 SOCKET 型別的控制代碼)

SOCKET socket(int af, int type, int protocol);

    1)af 為地址族(Address Family),也就是 IP 地址型別,常用的有 AF_INET(IPv4) 和 AF_INET6(IPv6) 。

    2)type 為資料傳輸方式,常用的有 SOCK_STREAM (面向連線的資料傳輸方式)和 SOCK_DGRAM(無連線的資料傳輸方式)。

    3)protocol 表示傳輸協議,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分別表示 TCP 傳輸協議和 UDP 傳輸協議。

4. bind() 函式伺服器端將套接字與特定的IP地址和埠繫結;connect() 函式客戶端用來建立連線

   1) bind()原型:

int bind(SOCKET sock, const struct sockaddr *addr, int addrlen); 

    2)connet()原型:

int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);

sock 為 socket 檔案描述符,addr 為 sockaddr 結構體變數的指標,addrlen 為 addr 變數的大小,可由 sizeof() 計算得出

sockaddr_in 結構體,成員變數如下:

struct sockaddr_in{
    sa_family_t     sin_family;   //地址族(Address Family),也就是地址型別
    uint16_t        sin_port;     //16位的埠號
    struct in_addr  sin_addr;     //32位IP地址
    char            sin_zero[8];  //不使用,一般用0填充
};

5.listen()函式讓套接字進入被動監聽狀態,它的原型為:

int listen(SOCKET sock, int backlog);

sock 為需要進入監聽狀態的套接字,backlog 為請求佇列的最大長度。

6.accept() 函式當套接字處於監聽狀態時,可以通過 accept() 函式來接收客戶端請求,它的原型為:

SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
sock 為伺服器端套接字,addr 為 sockaddr_in 結構體變數,addrlen 為引數 addr 的長度,可由 sizeof() 求得。

特別說明:listen() 只是讓套接字進入監聽狀態,並沒有真正接收客戶端請求,listen() 後面的程式碼會繼續執行,直到遇到 accept()。accept() 會阻塞程式執行(後面程式碼不能被執行),直到有新的請求到來。

7. send() 函式傳送資料,recv() 函式接受資料

    1)send()原型:

int send(SOCKET sock, const char *buf, int len, int flags);

     2)recv()原型:

int recv(SOCKET sock, char *buf, int len, int flags);

sock 為要傳送資料的套接字,buf 為要傳送的資料的緩衝區地址,len 為要傳送的資料的位元組數,flags 為傳送資料時的選項。

8.closesocket()函式關閉套接字,原型為:

void closesocket(SOCKET Sock);

9.WSACleanup()函式終止 DLL 的使用。