I/O复用Select函数的UDP和TCP客户端和服务端

本文介绍了如何利用I/O复用模型(Select函数)构建一个单线程服务端,该服务端能同时处理多个UDP和TCP客户端的时间同步请求。服务端接收请求后,显示客户端的IP和端口并回传时间信息。客户端通过发送特定字符串启动时间同步,TCP客户端在获取时间后断开连接,UDP客户端则无须断开。实验结果显示,服务端可在同一端口处理两种协议的数据,但在修改本地时间时可能遇到权限问题。作者从实践中加深了对TCP和UDP处理方式区别的理解,以及SetLocalTime函数的应用注意事项。
摘要由CSDN通过智能技术生成

 I/O复用Select函数的UDP和TCP客户端和服务端

 要求

- 服务端采用I/O复用模型(SELECT函数),接受客户端的时间同步请求

- 服务端采用单线程,但是要同时能够接受多个客户端的连接请求

- 服务端显示客户端的IP和端口,并回送时间信息

- 客户端包括UDP客户端和TCP客户端

 设计思路

主要是在书上的代码框架上进行修改,增加功能

- 客户端除了基本的创建套接字等步骤之外,先发送一个字符串,表示开始请求时间数据,然后就等待接收时间信息,然后修改本地时间

- 对于修改本地时间,首先要将受到的时间信息处理之后放入一个SYSTEMTIME类型的变量中,然后调用SetLocalTime()来进行修改

int set_local_time(const char *time_string)
{
    SYSTEMTIME system_time = { 0 };
    char year[4 + 1] = { 0 };
    char month[2 + 1] = { 0 };
    char day[2 + 1] = { 0 };
    char hour[2 + 1] = { 0 };
    char minute[2 + 1] = { 0 };
    char second[2 + 1] = { 0 };
    char Dayofweek[2] = { 0 };
    char Mill[2 + 1] = { 0 };
    int index = 0;

    strncpy(year, time_string + index, 4);
    index += 4;
    strncpy(month, time_string + index, 2);
    index += 2;
    strncpy(Dayofweek, time_string + index, 1);
    index += 1;
    strncpy(day, time_string + index, 2);
    index += 2;
    strncpy(hour, time_string + index, 2);
    index += 2;
    strncpy(minute, time_string + index, 2);
    index += 2;
    strncpy(second, time_string + index, 2);
    index += 2;
    strncpy(Mill, time_string + index, 2);
    index += 2;

    
    system_time.wYear = atoi(year);
    system_time.wMonth = atoi(month);
    system_time.wDay = atoi(day);
    system_time.wHour = atoi(hour);
    system_time.wMinute = atoi(minute);
    system_time.wSecond = atoi(second);
    system_time.wDayOfWeek = atoi(Dayofweek);
    system_time.wSecond = atoi(Mill);

    SetLocalTime(&system_time);
    SetLocalTime(&system_time);
    if (!SetLocalTime(&system_time))
    {
        cout << "Set local time failed  " << WSAGetLastError()<<endl;
        return -1;
    }

    return 0;
}

- 对于TCP连接来说,在得到信息之后要断开连接,UDP不存在连接也就不需要断开

- 服务器端在发送的时候也要注意将时间数据先进行处理,以字符串的形式发送

//处理时间,将SYSTEMTIME数据处理成字符串,便于发送给客户端
void get_time()
{
    SYSTEMTIME system_time = { 0 };
    GetLocalTime(&system_time);
    memset(timedata, 0sizeof(timedata));
    int index = 0;
    char tmp4[4];
    char tmp2[2];

    memset(timedata, 015);
    _itoa((int)system_time.wYear, tmp4, 10);
    strcat(timedata, tmp4);
    
    _itoa((int)system_time.wMonth, tmp2, 10);
    strcat(timedata, tmp2);

    memset(tmp2, 0sizeof(tmp2));
    _itoa((int)system_time.wDayOfWeek, tmp2, 10);
    strcat(timedata, tmp2);

    _itoa((int)system_time.wDay, tmp2, 10);
    strcat(timedata, tmp2);

    _itoa((int)system_time.wHour, tmp2, 10);
    strcat(timedata, tmp2);

    _itoa((int)system_time.wMinute, tmp2, 10);
    strcat(timedata, tmp2);

    _itoa((int)system_time.wSecond, tmp2, 10);
    strcat(timedata, tmp2);
    _itoa((int)system_time.wMilliseconds, tmp2, 10);
    strcat(timedata, tmp2);
    cout << "Server Time : "<<timedata << endl;
    return;
}

- 为了要能够接受TCP和UDP数据,所以在同一个端口上要同时绑定连个SOCKET

    // 为TCP套接字绑定地址和端口号
    SOCKADDR_IN addrServ;
    addrServ.sin_family = AF_INET;
    addrServ.sin_port = htons(DEFAULT_PORT);        // 监听端口为DEFAULT_PORT
    addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    iResult = bind(TCPSocket, (const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN));
    if (iResult == SOCKET_ERROR)
    {
        printf("bind failed with error: %d\n", WSAGetLastError());
        closesocket(TCPSocket);
        WSACleanup();
        return 1;
    }

    // TCP连接监听套接字   
    iResult = listen(TCPSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR)
    {
        printf("listen failed !\n");
        closesocket(TCPSocket);
        WSACleanup();
        return -1;
    }

    // 为UDP套接字绑定地址和端口号
    addrServ1.sin_family = AF_INET;
    addrServ1.sin_port = htons(DEFAULT_PORT);        // 监听端口为DEFAULT_PORT
    addrServ1.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    iResult = bind(UDPSocket, (const struct sockaddr*)&addrServ1, sizeof(SOCKADDR_IN));
    if (iResult == SOCKET_ERROR)
    {
        printf("bind failed with error: %d\n", WSAGetLastError());
        closesocket(UDPSocket);
        closesocket(TCPSocket);
        WSACleanup();
        return 1;
    }

    //注册TCPSOCKET和UDPSOCKET
    fd_set fdRead, fdSocket;
    FD_ZERO(&fdSocket);
    FD_SET(TCPSocket, &fdSocket);
    FD_SET(UDPSocket, &fdSocket);

- 在SELECT函数执行的过程中,如果是TCPSOCKET的连接,就将新的连接请求放入集合中,那么在后面的循环中就能处理,如果是UDPSOCKET,那么就直接用创建的UDPSOCKET和客户端进行数据通信,如果是之前的新的SOCKET连接,那就进行时间请求的通信

- 在新的TCP连接请求达到的时候,输出IP地址和端口号

//有新的连接请求
                            AcceptSocket = accept(TCPSocket, (sockaddr FAR*)&addrClient, &addrClientlen);
                            if (AcceptSocket == INVALID_SOCKET)
                            {
                                printf("accept failed !\n");
                                closesocket(UDPSocket);
                                closesocket(TCPSocket);
                                WSACleanup();
                                return 1;
                            }
                            //成功接收新的TCP连接
                            cout << "Received time request from ( " << inet_ntoa(addrClient.sin_addr) << " :" << addrClient.sin_port << " )" << endl;

- 在接受到UDP数据的时候,显示数据来源的IP和端口号

//获取客户端的时间请求信息
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值