Socket编程 ——IO复用(时间请求)

一 实验内容

利用I/O复用模型实现一个时间同步服务器
1. 服务端采用I/O复用模型(select函数)接收客户端的时间同步请求;
服务端采用单线程,但要能同时接收多客户端的连接请求,显示客户端IP
2. 和端口,并向其回送时间信息。
3. 客户端尝试同时使用UDP和TCP来实现。
注:借助I/O复用模型,用单线程达到多线程的效果。

二 代码设计

Client
时间同步函数

SYSTEMTIME curtime;//声明结构体变量;
curtime.wHour = hour;
curtime.wMinute = min;
curtime.wSecond = sec;
//结构体变量curtime各项值别赋值
if (SetLocalTime(&curtime))
{
    cout << "yes";
}
else
{
    cout << "no";
}

TCP

//从服务器接受时间并输出
while (true)
{
    cin >> SendBuffer;
    Ret = send(ClientSocket, SendBuffer, (int)strlen(SendBuffer), 0);
    if (Ret == SOCKET_ERROR)
    {
        cout << "Send Info Error::" << GetLastError() << endl;
        break;
    }
    Ret = recv(ClientSocket,(char *)&hour , 4, 0);
    Ret = recv(ClientSocket, (char *)&min, 4, 0);
    Ret = recv(ClientSocket, (char *)&sec, 4, 0);
    cout << hour << min <<sec<<endl;
}

UDP

//从服务器接受时间并输出
while (1)
{
    cin >> sendData;
    sendto(sclient, sendData, strlen(sendData), 0, (sockaddr *)&sin, len);
    char recvData[255];
    int ret = recvfrom(sclient, (char *)&hour, 4, 0, (sockaddr *)&sin, &len);
    ret = recvfrom(sclient, (char *)&min, 4, 0, (sockaddr *)&sin, &len);
    ret = recvfrom(sclient, (char *)&sec, 4, 0, (sockaddr *)&sin, &len);
    if (ret > 0)
    {
        cout << hour << min << sec << endl;
    }
}

Server

// 创建用于监听的套接字   
    ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);//TCP监听套接字
    UDPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);//UDP套接字
//为两个套接字绑定同一个端口
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(DEFAULT_PORT);        // 监听端口为DEFAULT_PORT
addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
iResult1 = bind(ServerSocket, (const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN));
iResult2 = bind(UDPSocket, (const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN));
//将绑定过端口的套接字加入套接字集合fdSocket中,可以观察到TCP监听套接字在[0]中,UDP套接字在[1]中
fd_set fdRead, fdSocket;
FD_ZERO(&fdSocket);
FD_SET(ServerSocket, &fdSocket);
FD_SET(UDPSocket, &fdSocket);
//用select函数挑选有响应的套接字
fdRead = fdSocket;
iResult = select(0, &fdRead, NULL, NULL, NULL);
//向集合中添加新socket
if (fdSocket.fd_array[i] == ServerSocket)
{
    if (fdSocket.fd_count < FD_SETSIZE)
    {
        //同时复用的套接字数量不能大于FD_SETSIZE
        //有新的连接请求
        AcceptSocket = accept(ServerSocket, (sockaddr FAR*)&addrClient, &addrClientlen);
        if (AcceptSocket == INVALID_SOCKET)
        {
            printf("accept failed !\n");
            closesocket(ServerSocket);
            WSACleanup();
            return 1;
        }
        //增加新的连接套接字进行复用等待
        FD_SET(AcceptSocket, &fdSocket);
        printf("接收到新的连接:%s\n", inet_ntoa(addrClient.sin_addr));
    }
    else
    {
        printf("连接个数超限!\n");
        continue;
    }
}

发送数据阶段

//tcp数据处理
if (i != 1)
{
    memset(recvbuf, 0, recvbuflen);
    now = time(NULL);
    tm = localtime(static_cast<const time_t*>(&now));
    hour = tm->tm_hour;
    min = tm->tm_min;
    sec = tm->tm_sec;
    //获取时间并传送
    iResult = recv(fdSocket.fd_array[i], recvbuf, recvbuflen, 0);
    Ret = send(fdSocket.fd_array[i], (const char *)&hour, 4, 0);
    Ret = send(fdSocket.fd_array[i], (const char *)&min, 4, 0);
    Ret = send(fdSocket.fd_array[i], (const char *)&sec, 4, 0);
    if (Ret == SOCKET_ERROR)
    {
        cout << "Send Info Error::" << GetLastError() << endl;
        break;
            }
}
//udp数据处理
char recvData[255];
    int ret = recvfrom(UDPSocket, recvData, 255, 0, (sockaddr *)&remoteAddr, &nAddrLen);
    if (ret > 0)
    {
        recvData[ret] = 0x00;

    }
    //        char * sendData = "一个来自服务端的UDP数据包\n";  
    now = time(NULL);
    tm = localtime(static_cast<const time_t*>(&now));
    hour = tm->tm_hour;
    min = tm->tm_min;
    sec = tm->tm_sec;
    //获取时间并传送
    sendto(fdSocket.fd_array[i], (const char *)&hour, 4, 0, (sockaddr *)&remoteAddr, nAddrLen);
    sendto(fdSocket.fd_array[i], (const char *)&min, 4, 0, (sockaddr *)&remoteAddr, nAddrLen);
    sendto(fdSocket.fd_array[i], (const char *)&sec, 4, 0, (sockaddr *)&remoteAddr, nAddrLen);
    cout << "UDP时间发送成功!";

三 实验实现

UDP Client测试
这里写图片描述
TCP Client测试
这里写图片描述
TCP / UDP Client测试
这里写图片描述
修改时间成功
服务器的时间设为2013.5.5 5:17 此时客户端更改时间为下图

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Java 代码示例,演示如何使用 Socket 编程实现信道复用技术: ``` import java.net.*; import java.io.*; public class SocketMultiplexer { public static void main(String[] args) throws Exception { // 创建一个 ServerSocket,监听指定端口 ServerSocket serverSocket = new ServerSocket(12345); // 创建一个 Selector Selector selector = Selector.open(); // 将 ServerSocket 注册到 Selector 上,监听 OP_ACCEPT 事件 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // 阻塞等待事件发生 selector.select(); // 处理所有已经就绪的事件 Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> iter = selectedKeys.iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); iter.remove(); if (key.isAcceptable()) { // 处理 OP_ACCEPT 事件 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { // 处理 OP_READ 事件 SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); client.read(buffer); buffer.flip(); String message = new String(buffer.array()).trim(); System.out.println("Received message: " + message); } } } } } ``` 这个示例程序创建了一个 ServerSocket,监听端口 12345。然后创建了一个 Selector,并将 ServerSocket 注册到 Selector 上,监听 OP_ACCEPT 事件。在主循环中,程序阻塞等待事件发生,然后处理所有已经就绪的事件。如果事件是 OP_ACCEPT,说明有新的客户端连接,程序将客户端的 SocketChannel 注册到 Selector 上,监听 OP_READ 事件。如果事件是 OP_READ,说明有客户端发送了数据,程序读取数据并打印出来。 这个示例程序演示了如何使用 Socket 编程实现信道复用技术,通过 Selector 监听多个 SocketChannel 上的事件,从而实现了多路复用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值