1. 程式人生 > >C/S模型之UDP協議

C/S模型之UDP協議

lob socket 端口號 add 想要 span ipp 技術分享 get

技術分享

說明:
利用UDP協議,創建一個服務器和一個客戶端。兩者間進行通信。
由客戶端進行輸入內容,而服務器將接受的內容進行再一次返回,並顯示在服務端。

// UDP_Seversock.cpp : 定義控制臺應用程序的入口點。

#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#pragma comment (lib,"WS2_32.lib")

//服務器端
int _tmain(int argc, _TCHAR* argv[])
{
    //檢測版本
    WSADATA wsaData;
    
int nErr = WSAStartup(MAKEWORD(2, 2), &wsaData); if (nErr != 0) { printf("WSAStartup Failed %d", GetLastError()); return -1; } if (HIBYTE(wsaData.wVersion != 2 || wsaData.wVersion != 2)) { printf("WinSock2 version not is WS2_32"); WSACleanup();
return -1; } //開始 SOCKET sockSever = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //設置端口號和IP地址,INADDR_ANY是任意IP地址 SOCKADDR_IN sockSeverAddr; sockSeverAddr.sin_family = AF_INET; sockSeverAddr.sin_port = htons(10086); //sockSeverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); sockSeverAddr.sin_addr.S_un.S_addr = inet_addr("
127.0.0.1"); //將socket進行綁定 int nBindErr = bind(sockSever, (sockaddr*)&sockSeverAddr, sizeof(SOCKADDR_IN)); if (nBindErr == SOCKET_ERROR) { printf("bind Error!"); closesocket(sockSever); WSACleanup(); } printf("connect sucess!\n"); SOCKADDR_IN SenderAddr; int SenderAddrSize = sizeof (SenderAddr); char pRecvBuf[MAXBYTE] = { 0 }; char pPrintBuf[MAXBYTE] = { 0 }; while (1) { int nResult = recvfrom(sockSever, pRecvBuf, MAXBYTE, 0, (SOCKADDR *)&SenderAddr, &SenderAddrSize); if (nResult == SOCKET_ERROR) { printf("recvfrom failed with error "); } //重置,將IP地址和端口號,內容存放在pPrintBuf中 sprintf_s(pPrintBuf, "%s(%d):%s\n", inet_ntoa(sockSeverAddr.sin_addr), ntohs(sockSeverAddr.sin_port), pRecvBuf); //輸出在窗口 printf("%s\n", pPrintBuf); //客戶端請求退出 if (strcmp(pRecvBuf, "Q") == 0) { break; } int nSendResult = sendto(sockSever, pRecvBuf, MAXBYTE, 0, (sockaddr*)&SenderAddr, SenderAddrSize); if (nSendResult == SOCKET_ERROR) { printf("sendto Error!"); break; } } closesocket(sockSever); WSACleanup(); return 0; }

#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#pragma comment(lib,"WS2_32.lib")

//客戶端
int _tmain(int argc, _TCHAR* argv[])
{
    //socket版本檢測
    WSAData wsaData;
    int nErr=WSAStartup(MAKEWORD(2, 2), &wsaData); 
    if (nErr != 0)
    {
        printf("WSAStartup Failed %d", GetLastError());
        return -1;
    }
    if (HIBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2)
    {
        printf("WinSock2 version not is WS2_32");
        WSACleanup();
            return -1;
    }

    //程序開始
    SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    //設定端口和IP地址,127.0.0.1是本機回環地址
    SOCKADDR_IN sockAddr;
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(10086);
    sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    int RecvAddrSize = sizeof (SOCKADDR_IN);

    //int nBindErr=bind(sockClient, (sockaddr*)&sockAddr, sizeof(SOCKADDR_IN));
    //if (nBindErr == SOCKET_ERROR)
    //{
    //    printf("bind Error!");
    //    closesocket(sockClient);
    //    WSACleanup();
    //    return -1;
    //}

    printf("connect sucess!\n");

    char pSendBuf[MAXBYTE] = { 0 };        //發送服務器的內容
    char pRecvBuf[MAXBYTE] = { 0 };        //接受服務器返回的內容

    while (1)
    {
        gets_s(pSendBuf);

        //發送給服務器
        int nResule = sendto(sockClient, pSendBuf, MAXBYTE, 0, (SOCKADDR *)& sockAddr, RecvAddrSize);
        if (nResule == SOCKET_ERROR)
        {
            printf("sendto  Error!");
        }

        //接受服務器返回的內容
        int nRecvErr = recvfrom(sockClient, pRecvBuf, MAXBYTE, 0, (SOCKADDR *)& sockAddr, &RecvAddrSize);
        if (nRecvErr == SOCKET_ERROR)
        {
            break;
        }

        //服務器返回內容
        printf("return from sever:%s\n", pRecvBuf);

    }
    closesocket(sockClient);
    WSACleanup();
    return 0;
}

註意點;
問題:為什麽UDP協議的客戶端的socket不需要綁定IP地址和端口號?
解析; 
書上都是這麽說的,UDP客戶端不用綁定IP和端口,操作系統會給它自動分配端口。。。。

但是雖然沒有顯示綁定,但是操作系統卻似乎做了些隱蔽的事情。

首先,在客戶端,fd = socket(AF_INET, SOCK_DGRAM, 0),然後就想在此fd下進行recvfrom是收不到對方(假設對方就是服務器吧)的消息是辦不到的,
其實想想也很容易明白,這是fd未和任何端口、IP產生關聯要是這樣都能收到消息,那真要亂套了。想要在沒綁定的情況下受到服務器發來的消息,
首先客戶端得通過fd描述符首先向服務器發信息,然後這時在fd下進行阻塞recvfrom就能收到消息了,如果再在客戶端上fd 1= socket(AF_INET, SOCK_DGRAM, 0),
想在fd1 上進行recvfrom依然收不到消息,因為fd和服務器同過信,但fd1沒有,所以fd1收不到,但是從fd1向服務器發消息沒問題!所一總結一下就是,
只有當已通過fd向服務器發送了消息時(並且已經發通了),才能在fd處收到服務器發回來的消息,但是向服務器發送消息就不需要。
所以說操作系統在此做了些隱蔽的事情,當fd首先向服務器發消息時客戶端自動選折IP和一個PORT與該fd關聯了起來,(我覺得相當於背後還是綁定了一樣)。
而後面創建的fd1和之前的fd他們出客戶端的PORT是不同的(我在服務器端檢測了一下),所以通過fd向服務器發了消息但想在新建立的fd1下去recvfrom收不到消息。

 另外,只能對一個socket描述符綁定一次,不能綁定多次,除非前面已經將該描述符close了。

 反過來一個端口也只能被綁定到同一個socket描述符上,除非他們使用的不同的協議。

註意:如果客戶端綁定了,那麽在服務器端將無法輸出任何東西,在recvfrom()該函數阻塞。

1.每一次都要進行版本檢測。
2.頭文件的包含,#include <WinSock2.h>一定要在#include <Windows.h>的前面。
如:
#include <WinSock2.h>
#include <Windows.h>

C/S模型之UDP協議