使用socket實現TCP和UDP傳輸

轉載自:https://blog.csdn.net/timmiy/article/details/51946093windows

             https://blog.csdn.net/timmiy/article/details/52016946數組

socket由IP地址和端口號組成,能夠經過TCP,UDP,IP協議實現不一樣虛擬機或不一樣計算機之間的通訊,效率較高。安全

(一)運用TCP協議進行socket通訊

TCP是面向鏈接的,它在進行通訊以前,須要雙方先進行溝通,而後才能進行通訊。並且TCP是以數據流的方式進行數據傳遞,會自動的進行拆包和組包的過程。因此TCP的鏈接是比較可靠的,可是它的傳輸速度也所以相對較慢。接下來分別介紹服務端和客戶端,看下如何在windows系統中用C++語言實現TCP通訊。服務器

在windows中,要想進行socket網絡操做,必須包含一個名叫作WinSock2.h(或者WinSock.h),若是包含的是WinSock2.h則必須在windows.h以前,不然會產生一些重定義的編譯錯誤。包含完頭文件以後,還要連接一個庫文件ws2_32.lib,完成以後,咱們就能夠開始進行TCP服務端和客戶端的編寫了。(若是使用Visual Studio編譯器運行,VS會自動生成.h文件,不須要本身手動包含和連接庫文件。)網絡

1.1  服務端

首先給出服務端的實現思路:socket

1.初始化socket環境 -> 2.建立服務器socket -> 3.初始化端口和ip地址調用bind進行綁定 -> 4.調用listen進行監聽 -> 5.調用accept接收客戶端的請求 -> 6.調用recv和send與客戶端進行通訊 -> 7.調用WSACleanup及closesocket關閉網絡環境和socket函數

下面是具體的實現示例程序:spa

 
  1.  
    #include <stdio.h>
  2.  
    #include <winsock2.h> // 必須包含windwos.h以前
  3.  
    #include <Windows.h>
  4.  
     
  5.  
    #pragma comment(lib,"ws2_32.lib")
  6.  
     
  7.  
    #define PORT 6000
  8.  
    DWORD WINAPI clientProc(LPARAM lparam) //通訊接收和發送數據過程函數(recv、send)
  9.  
    {
  10.  
    SOCKET sockClient = (SOCKET)lparam;
  11.  
    char buf[1024];
  12.  
    while (TRUE)
  13.  
    {
  14.  
    memset(buf, 0, sizeof(buf));
  15.  
     
  16.  
    // 接收客戶端的一條數據
  17.  
    int ret = recv(sockClient, buf, sizeof(buf), 0);
  18.  
    //檢查是否接收失敗
  19.  
    if (SOCKET_ERROR == ret)
  20.  
    {
  21.  
    printf("socket recv failed\n");
  22.  
    closesocket(sockClient);
  23.  
    return -1;
  24.  
    }
  25.  
    else
  26.  
    {
  27.  
    printf("%s\r\n", buf);
  28.  
    }
  29.  
    // 0 表明客戶端主動斷開鏈接
  30.  
    if (ret == 0)
  31.  
    {
  32.  
    printf("client close connection\n");
  33.  
    closesocket(sockClient);
  34.  
    return -1;
  35.  
    }
  36.  
     
  37.  
    // 發送數據
  38.  
    char *p = "hello client";
  39.  
    ret = send(sockClient, p, strlen(p), 0);
  40.  
    //檢查是否發送失敗
  41.  
    if (SOCKET_ERROR == ret)
  42.  
    {
  43.  
    printf("socket send failed\n");
  44.  
    closesocket(sockClient);
  45.  
    return -1;
  46.  
    }
  47.  
    }
  48.  
    closesocket(sockClient);
  49.  
    return 0;
  50.  
    }
  51.  
    bool InitNetEnv()    //網絡環境初始化函數
  52.  
    {
  53.  
    // 進行網絡環境的初始化操做
  54.  
    WSADATA wsa;
  55.  
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
  56.  
    {
  57.  
    printf("WSAStartup failed\n");
  58.  
    return false;
  59.  
    }
  60.  
    return true;
  61.  
    }
  62.  
     
  63.  
    int main(int argc, char * argv[])
  64.  
    {
  65.  
    if (!InitNetEnv()) //Step1:初始化網絡環境
  66.  
    {
  67.  
    return -1;
  68.  
    }
  69.  
     
  70.  
    // Step2:初始化完成,建立一個TCP的socket
  71.  
    //socket(協議域,指定socket類型,指定協議)
  72.  
    SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  73.  
    //檢查是否建立失敗
  74.  
    if (sServer == INVALID_SOCKET)
  75.  
    {
  76.  
    printf("socket failed\n");
  77.  
    return -1;
  78.  
    }
  79.  
    printf("Create socket OK\n");
  80.  
     
  81.  
    //Step3:進行綁定操做(bind)
  82.  
    SOCKADDR_IN addrServ;
  83.  
    addrServ.sin_family = AF_INET; // 協議簇爲IPV4的
  84.  
    addrServ.sin_port = htons(PORT); // 端口 由於本機是小端模式,網絡是大端模式,調用htons把本機字節序轉爲網絡字節序
  85.  
    addrServ.sin_addr.S_un.S_addr = INADDR_ANY; // ip地址,INADDR_ANY表示綁定電腦上全部網卡IP
  86.  
    //完成綁定操做
  87.  
    //bind(socket描述字, 綁定給listenfd的協議地址,地址長度)
  88.  
    int ret = bind(sServer, (sockaddr *)&addrServ, sizeof(sockaddr));
  89.  
    //檢查綁定是否成功
  90.  
    if (SOCKET_ERROR == ret)
  91.  
    {
  92.  
    printf("socket bind failed\n");
  93.  
    WSACleanup(); // 釋放網絡環境
  94.  
    closesocket(sServer); // 關閉網絡鏈接
  95.  
    return -1;
  96.  
    }
  97.  
    printf("socket bind OK\n");
  98.  
     
  99.  
    // Stpe4:綁定成功,進行監聽(listen)
  100.  
    //listen(socket描述字, socket能夠排隊的最大鏈接個數)
  101.  
    ret = listen(sServer, 10);
  102.  
    //檢查是否監聽成功
  103.  
    if (SOCKET_ERROR == ret)
  104.  
    {
  105.  
    printf("socket listen failed\n");
  106.  
    WSACleanup();
  107.  
    closesocket(sServer);
  108.  
    return -1;
  109.  
    }
  110.  
    printf("socket listen OK\n");
  111.  
     
  112.  
    // 監聽成功
  113.  
    sockaddr_in addrClient; // 用於保存客戶端的網絡節點的信息
  114.  
    int addrClientLen = sizeof(sockaddr_in);
  115.  
    while (TRUE)
  116.  
    {
  117.  
    //新建一個socket,用於客戶端
  118.  
    SOCKET *sClient = new SOCKET;
  119.  
    //Step5:等待客戶端的鏈接(accept)
  120.  
    //accept(服務器的描述字,指向客戶端的協議地址, 協議地址的長度)
  121.  
    *sClient = accept(sServer, (sockaddr*)&addrClient, &addrClientLen);
  122.  
    if (INVALID_SOCKET == *sClient)
  123.  
    {
  124.  
    printf("socket accept failed\n");
  125.  
    WSACleanup();
  126.  
    closesocket(sServer);
  127.  
    delete sClient;
  128.  
    return -1;
  129.  
    }
  130.  
    //Step6:建立線程爲客戶端作數據收發
  131.  
    CreateThread(0, 0, (LPTHREAD_START_ROUTINE)clientProc, (LPVOID)*sClient, 0, 0);
  132.  
    }
  133.  
     
  134.  
    closesocket(sServer); //關閉網絡環境和socket
  135.  
    WSACleanup();
  136.  
    return 0;
  137.  
    }

下面對代碼中的實現函數進行說明。操作系統

1)首先,要進行網絡操做,咱們先要進行一下網絡環境的初始化。WSAStartup函數就是用來初始化網絡環境的。其聲明以下:.net

 
  1.  
    int WSAStartup(
  2.  
    WORD wVersionRequested, //版本號,通常使用2.2版本
  3.  
    LPWSADATA lpWSAData <span style="white-space:pre"> </span>//WSAData地址
  4.  
    );

函數的第二個參數,接收一個WSAData結構的指針,該結構裏邊包含了版本號,咱們傳遞的版本號會對該結構裏邊的版本號進行初始化。

2)初始化完成以後,咱們須要建立一個socket(套接字),這個套接字至關於管道,用於客戶端和服務端的鏈接。調用socket函數咱們能夠建立一個套接字,聲明以下:

 
  1.  
    SOCKET socket(
  2.  
    int af, //IP協議簇
  3.  
    int type, //套接字類型,TCP應該用SOCK_STREAM
  4.  
    int protocol<span style="white-space:pre"> </span> //協議
  5.  
    );

其實,socket也是一個內核對象,可是它沒有內核對象所擁有的明顯標誌,安全屬性。

3)建立好套接字後呢,咱們須要告訴操做系統須要在哪一個地址和端口上進行網絡操做,至關於管道通訊中綁定到標準輸入輸出口上。綁定的時候,須要有一個SOCKADDR_IN這個結構體,聲明以下:

 
  1.  
    struct sockaddr_in{
  2.  
    short sin_family;//協議簇
  3.  
    unsigned short sin_port;//端口
  4.  
    struct in_addr sin_addr;//ip地址
  5.  
    char sin_zero[8];//爲了設置和SOCKADDR結構等長的補充字節
  6.  
    };

還有一個SOCKADDR結構和上面這個的功能徹底同樣,可是SOCKADDR這個結構裏邊只有兩個成員,一個是協議簇,一個是14個字節的char數組,爲了讓咱們更好的編寫代碼,因而將char數組拆解成SOCKADDR_IN 中後三個成員。

初始化完端口,地址等信息後,須要調用bind函數,來完成綁定操做,聲明以下:

 
  1.  
    int bind(
  2.  
    SOCKET s, //咱們建立的那個socket
  3.  
    const struct sockaddr FAR *name, //sockaddr結構指針
  4.  
    int namelen //sockaddr長度
  5.  
    );
4)綁定以後,咱們還須要調用listen函數來進行監聽操做,這個操做呢,就至關於門衛同樣了,若是有人來,就告訴你一聲,這就是監聽。該函數聲明以下:
 
  1.  
    int listen(
  2.  
    SOCKET s, //咱們建立的socket
  3.  
    int backlog //最大鏈接的隊列長度
  4.  
    );
第二個參數backlog呢,咱們通常不要給的太大,這就比如你去交電費,還要進行排隊等候,若是排隊的人多了,這就會給你留下很差的體驗,所以隨便給個10,100的就好了。

5)監聽完成以後,咱們就能夠進行接收客戶端的鏈接了,咱們須要調用accept這個函數來進行接客。聲明以下:

 
  1.  
    SOCKET accept(
  2.  
    SOCKET s, //咱們監聽的那個socket
  3.  
    struct sockaddr FAR *addr, //咱們須要傳遞一個sockaddr的地址,用於保存客戶端的地址
  4.  
    int FAR *addrlen //sockaddr的長度指針
  5.  
    );

6)接完客以後,咱們就能夠進行通訊了,須要調用recv和send兩個函數來進行收發數據,它們的聲明以下:

 
  1.  
    int recv(
  2.  
    SOCKET s, //客戶端的socket
  3.  
    char FAR *buf, //接收的緩衝區
  4.  
    int len, //緩衝區的大小
  5.  
    int flags //標誌位,通常爲0
  6.  
    );
  7.  
     
  8.  
    int send(
  9.  
    SOCKET s, //客戶端的socket
  10.  
    const char FAR *buf, //發送數據的緩衝區
  11.  
    int len, //緩衝區的大小
  12.  
    int flags //標誌位,通常爲0
  13.  
    );

7)當咱們傳輸完數據後,應該調用WSACleanup和closesocket來進行關閉網絡環境和套接字。聲明以下:

 
  1.  
    int WSACleanup (void);
  2.  
     
  3.  
    int closesocket(
  4.  
    SOCKET s //要關閉的套接字
  5.  
    );

1.2 客戶端

首先給出使用TCP協議在客戶端的思路:

1.初始化socket環境 -> 2.建立客戶端socket -> 3.調用connect鏈接指定的服務器 -> 4.調用recv和send與服務端進行通訊 -> 5.調用WSACleanup及closesocket關閉網絡環境和socket

下面是具體的實現程序:

 
  1.  
    #include <stdio.h>
  2.  
    #include <winsock2.h>
  3.  
    #include <Windows.h>
  4.  
     
  5.  
    #pragma comment(lib,"ws2_32.lib")
  6.  
    #define PORT 6000
  7.  
     
  8.  
    int main(int argc, char * argv[])
  9.  
    {
  10.  
    //Step1:初始化網絡環境
  11.  
    WSADATA wsa;
  12.  
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
  13.  
    {
  14.  
    printf("WSAStartup failed\n");
  15.  
    return -1;
  16.  
    }
  17.  
     
  18.  
    // Step2:初始化完成,建立一個TCP的socket
  19.  
    //socket(協議域,指定socket類型,指定協議)
  20.  
    SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  21.  
    if (sServer == INVALID_SOCKET)
  22.  
    {
  23.  
    printf("socket failed\n");
  24.  
    return -1;
  25.  
    }
  26.  
     
  27.  
    //Step3:指定鏈接的服務端信息(bind)
  28.  
    SOCKADDR_IN addrServ;
  29.  
    addrServ.sin_family = AF_INET;
  30.  
    addrServ.sin_port = htons(PORT);
  31.  
    //客戶端只須要鏈接指定的服務器地址,127.0.0.1是本機的迴環地址
  32.  
    addrServ.sin_addr.S_un.S_addr = inet_addr("10.170.54.98");
  33.  
     
  34.  
    // 服務器Bind 客戶端是進行鏈接
  35.  
    //connect(客戶端的socket描述字, 服務器的socket地址, 服務器地址長度)
  36.  
    int ret = connect(sServer, (SOCKADDR*)&addrServ, sizeof(SOCKADDR));//開始鏈接
  37.  
    if (SOCKET_ERROR == ret)
  38.  
    {
  39.  
    printf("socket connect failed\n");
  40.  
    WSACleanup();
  41.  
    closesocket(sServer);
  42.  
    return -1;
  43.  
    }
  44.  
     
  45.  
    //Step4:鏈接成功後,就能夠進行通訊了(send,recv)
  46.  
    char szBuf[1024];
  47.  
    memset(szBuf, 0, sizeof(szBuf));
  48.  
    sprintf_s(szBuf, sizeof(szBuf), "Hello server");
  49.  
    //當服務端是recv的時候,客戶端就須要send,若兩端同時進行收發則會卡在這裏,由於recv和send都是阻塞的
  50.  
    ret = send(sServer, szBuf, strlen(szBuf), 0);
  51.  
    if (SOCKET_ERROR == ret)
  52.  
    {
  53.  
    printf("socket send failed\n");
  54.  
    closesocket(sServer);
  55.  
    return -1;
  56.  
    }
  57.  
     
  58.  
    ret = recv(sServer, szBuf, sizeof(szBuf), 0);
  59.  
    if (SOCKET_ERROR == ret)
  60.  
    {
  61.  
    printf("socket recv failed\n");
  62.  
    closesocket(sServer);
  63.  
    return -1;
  64.  
    }
  65.  
    printf("%s\n", szBuf);
  66.  
     
  67.  
    closesocket(sServer); //Step5:關閉已鏈接socket描述字
  68.  
    WSACleanup();
  69.  
    system("pause");
  70.  
    return 0;
  71.  
    }

下面對代碼中的函數進行解釋。

(1-2)客戶端比較簡單,前面的部分和服務端都基本相同(初始化、創建socket)

(3)在綁定操做上會有所差異。服務端綁定的IP地址是本機全部網卡的IP,而客戶端只須要綁定一個便可,由於對客戶端來講,咱們只需鏈接指定的服務器。賦值完SOCKADDR_IN結構以後,服務端會調用bind函數,而客戶端呢,須要調用connect函數,其聲明以下:

 
  1.  
    int connect(
  2.  
    SOCKET s, //要進行鏈接的socket
  3.  
    const struct sockaddr FAR *name, //SOCKADDR結構地址
  4.  
    int namelen //SOKADDR大小
  5.  
    );
(4)鏈接成功後,就能夠和服務端進行通訊了,調用recv和send來進行收發數據。須要注意的是,若是服務端程序先進行recv操做,則咱們應該在客戶端先進行send操做,若兩個同時進行相同的操做的話,則會卡在當前的位置,由於recv和send都是阻塞型的函數。

(5)當通訊完以後,就能夠關閉鏈接了。文章開頭講過,當客戶端和服務端剛開始鏈接的時候呢,二者會先進行溝通,這個溝通須要3個步驟來完成,咱們稱之爲3次握手,一樣的關閉鏈接的時候,須要進行4個步驟來完成,咱們稱之爲4次握手。若是你是粗暴型的,直接拔網線呢,它也會完成其中的兩次步驟,做爲應用層開發,並不須要深究其中的原理,若感興趣,可自行查找資料。

在兩個Visual Studio中依次運行服務端及客戶端程序,獲得socket通訊結果以下:

(二)運用UDP協議進行socket通訊

相比TCP來講,UDP相對比較簡單,剛開始的時候,和TCP同樣都須要先進行網絡環境的初始化,即調用WSAStartup函數。而後呢,咱們也須要建立一個socket,這個socket和TCP的那個socket不一樣,上篇提過TCP建立一個socket調用socket函數時,第二個參數爲SOCK_STREAM,而UDP則須要給定一個SOCK_DGRAM,而後在第三個參數上給一個IPPROTO_UDP,這樣咱們就建立好了一個UDP的socket。

接下來,也和TCP同樣,指定SOCKADDR_IN的地址信息(端口,ip),指定完以後呢,如果客戶端,則能夠直接就進行通訊了,如果服務端,則還須要增長一步bind操做,當咱們調用bind函數,進行綁定後,服務端就能夠和客戶端進行通訊了。而TCP的服務端還有兩個步驟,一個是listen,一個是accept,UDP省略了這兩個步驟。

2.1  服務端

首先給出使用UDP協議實現socket通訊的服務端的實現思路:

1.初始化socket環境 -> 2.建立服務器socket -> 3.初始化端口和ip地址調用bind進行綁定 -> 4.調用recvfrom和sendto與客戶端進行通訊 -> 5.調用WSACleanup及closesocket關閉網絡環境和socket

下面是具體的實現代碼:

 
  1.  
    #include <stdio.h>
  2.  
    #include <winsock2.h>
  3.  
    #include <Windows.h>
  4.  
     
  5.  
    #pragma comment(lib,"ws2_32.lib")
  6.  
    #define PORT 6000
  7.  
     
  8.  
    int main(int argc, char* argv[])
  9.  
    {
  10.  
    //Step1:初始化網絡環境
  11.  
    WSADATA wsa;
  12.  
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
  13.  
    {
  14.  
    printf("WSAStartup failed\n");
  15.  
    return -1;
  16.  
    }
  17.  
     
  18.  
    //Step2:創建一個UDP的socket
  19.  
    //創建socket參數:socket(協議域,指定socket類型,指定協議)(和TCP協議後兩個參數不一樣,都爲IP協議族)
  20.  
    SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  21.  
    if (sock == SOCKET_ERROR)
  22.  
    {
  23.  
    printf("create socket failed\n");
  24.  
    return -1;
  25.  
    }
  26.  
     
  27.  
    //Step3:綁定地址信息
  28.  
    sockaddr_in serverAddr;
  29.  
    serverAddr.sin_family = AF_INET; // 協議簇爲IPV4的
  30.  
    serverAddr.sin_port = htons(PORT); // 端口 由於本機是小端模式,網絡是大端模式,調用htons把本機字節序轉爲網絡字節序
  31.  
    serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // ip地址,INADDR_ANY表示綁定電腦上全部網卡IP
  32.  
     
  33.  
    //bind(socket描述字, 綁定給listenfd的協議地址,地址長度)
  34.  
    bind(sock, (sockaddr*)&serverAddr, sizeof(sockaddr));
  35.  
    //Step5:與客戶端進行通訊
  36.  
    char buf[512];
  37.  
    while (TRUE)
  38.  
    {
  39.  
    memset(buf, 0, 512);
  40.  
    // 網絡節點的信息,用來保存客戶端的網絡信息
  41.  
    sockaddr_in clientAddr;
  42.  
    memset(&clientAddr, 0, sizeof(sockaddr_in));
  43.  
     
  44.  
    int clientAddrLen = sizeof(sockaddr);
  45.  
    //接收客戶端發來的數據
  46.  
    //recvfrom參數:socket名稱,接收數據的緩衝區,緩衝區大小,標誌位(調用操做方式),sockaddr結構地址,sockaddr結構大小地址
  47.  
    //sockaddr地址用來保存從哪裏發來,和發送到哪裏的地址信息
  48.  
    int ret = recvfrom(sock, buf, 512, 0, (sockaddr*)&clientAddr, &clientAddrLen);
  49.  
     
  50.  
    //inet_ntoa函數轉化爲ip,ntohs函數轉化爲端口號
  51.  
    printf("Recv msg:%s from IP:[%s] Port:[%d]\n", buf, inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
  52.  
     
  53.  
    // 發一個數據包返回給客戶
  54.  
    //sendto參數:socket名稱,發送數據的緩衝區,緩衝區大小,標誌位(調用操做方式),sockaddr結構地址,sockaddr結構大小地址
  55.  
    sendto(sock, "Hello World!", strlen("Hello World!"), 0, (sockaddr*)&clientAddr, clientAddrLen);
  56.  
    printf("Send msg back to IP:[%s] Port:[%d]\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
  57.  
    }
  58.  
    return 0;
  59.  
    }

前面提到TCP進行數據的收發是經過recv和send兩個API來進行數據的收發的。而UDP也須要兩個函數,叫作recvfrom和sendto,這兩個和TCP那兩個有點不一樣,其聲明以下:

 
  1.  
    int recvfrom(
  2.  
    SOCKET s, //socket
  3.  
    char FAR* buf, <span style="white-space:pre"> </span>//接收數據的緩衝區
  4.  
    int len, //緩衝區的大小
  5.  
    int flags, //標誌位,調用操做方式
  6.  
    struct sockaddr FAR *from, //sockaddr結構地址
  7.  
    int FAR *fromlen //sockaddr結構大小地址
  8.  
    );
  9.  
    int sendto(
  10.  
    SOCKET s, //socket
  11.  
    const char FAR *buf, //發送數據的緩衝區
  12.  
    int len, //緩衝區大小
  13.  
    int flags, //標誌位,調用操做方式
  14.  
    const struct sockaddr FAR *to, //sockaddr結構地址
  15.  
    int tolen //sockaddr結構大小地址
  16.  
    );

注意,這兩個函數裏邊有一個sockaddr結構地址,它是用來保存該數據發送者的信息的。上篇提過,TCP是面向鏈接的,它在通訊以前須要進行三次握手來肯定雙方是否已經準備好了。所以,雙方很清楚數據是從哪裏來的。而UDP是面向數據包的,所以就好像寄快遞同樣,你必須在快遞上寫一張紙條,上面填好姓名,地址等信息,填好以後,接收者才知道該東西是由誰寄過來的。所以,上面兩個函數提供了sockaddr結構的地址,用於保存從哪裏發來的和發送到哪裏的地址信息。

2.2 客戶端

給出使用UDP協議實現socket通訊的客戶端的示例代碼:

1.初始化socket環境 -> 2.建立客戶端socket -> 3.調用recvfrom和sendto與服務端進行通訊 -> 4.WSACleanup及closesocket關閉網絡環境和socket

下面是具體的實現程序:

 
  1.  
    #include <stdio.h>
  2.  
    #include <winsock2.h>
  3.  
    #include <Windows.h>
  4.  
     
  5.  
    #pragma comment(lib,"ws2_32.lib")
  6.  
    #define PORT 6000
  7.  
    int main(int argc, char* argv[])
  8.  
    {
  9.  
    //Step1:初始化網絡環境
  10.  
    WSADATA wsa;
  11.  
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
  12.  
    {
  13.  
    printf("WSAStartup failed\n");
  14.  
    return -1;
  15.  
    }
  16.  
     
  17.  
    //Step2:創建一個UDP的socket
  18.  
    //創建socket參數:socket(協議域,指定socket類型,指定協議)(和TCP協議後兩個參數不一樣,都爲IP協議族)
  19.  
    SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  20.  
    if (sockClient == INVALID_SOCKET)
  21.  
    {
  22.  
    printf("create socket failed\n");
  23.  
    return -1;
  24.  
    }
  25.  
     
  26.  
    // 申明一個網絡地址信息的結構體,保存服務器的地址信息
  27.  
    sockaddr_in addr = { 0 };
  28.  
    addr.sin_family = AF_INET; // 協議簇爲IPV4的
  29.  
    addr.sin_port = htons(PORT); // 端口 由於本機是小端模式,網絡是大端模式,調用htons把本機字節序轉爲網絡字節序
  30.  
    addr.sin_addr.S_un.S_addr = inet_addr("10.170.54.98"); // 服務器的ip地址
  31.  
    //Step3:與服務端進行通訊
  32.  
    char buf[] = "client test!";
  33.  
    //發送數據
  34.  
    //sendto參數:socket名稱,接收數據的緩衝區,緩衝區大小,標誌位(調用操做方式),sockaddr結構地址,sockaddr結構大小地址
  35.  
    int dwSent = sendto(sockClient, buf, strlen(buf), 0, (SOCKADDR *)&addr, sizeof(SOCKADDR));
  36.  
    if (dwSent == 0)
  37.  
    {
  38.  
    printf("send %s failed\n", buf);
  39.  
    return -1;
  40.  
    }
  41.  
    printf("send msg:%s\n", buf);
  42.  
     
  43.  
    char recvBuf[512];
  44.  
    memset(recvBuf, 0, 512);
  45.  
     
  46.  
    sockaddr_in addrSever = { 0 };
  47.  
    int nServerAddrLen = sizeof(sockaddr_in);
  48.  
     
  49.  
    // 接收數據
  50.  
    //recvfrom參數:socket名稱,接收數據的緩衝區,緩衝區大小,標誌位(調用操做方式),sockaddr結構地址,sockaddr結構大小地址
  51.  
    int dwRecv = recvfrom(sockClient, recvBuf, 512, 0, (SOCKADDR *)&addrSever, &nServerAddrLen);
  52.  
    printf("Recv msg from server : %s\n", recvBuf);
  53.  
     
  54.  
    //Step4:關閉SOCKET鏈接
  55.  
    closesocket(sockClient);
  56.  
     
  57.  
    //清理網絡環境
  58.  
    WSACleanup();
  59.  
    system("pause");
  60.  
    return 0;
  61.  
    }

在兩個Visual Studio中依次運行服務端及客戶端程序,獲得socket通訊結果以下:

相關文章
相關標籤/搜索