C/S模型之UDP协议

Posted _拥你一生,如沐星辰_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/S模型之UDP协议相关的知识,希望对你有一定的参考价值。

 

说明:
利用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协议的主要内容,如果未能解决你的问题,请参考以下文章

java网络编程

java网络编程

No.28TCP&UDP

python之socket(套接字)

C/S模型之TCP协议

网络编程 socket模块 tcp协议 udp协议