📜  TCP 和 UDP 服务器使用 select

📅  最后修改于: 2022-05-13 01:57:02.515000             🧑  作者: Mango

TCP 和 UDP 服务器使用 select

先决条件: TCP UDP

在之前的文章中,我们已经看到了TCP服务器和UDP服务器。但现在我们可以将并发 TCP 回显服务器和迭代 UDP 服务器组合成一个使用 select 复用 TCP 和 UDP 套接字的服务器。

Select函数用于在 TCP 和 UDP 套接字之间进行选择。该函数指示内核等待多个事件中的任何一个发生,并仅在一个或多个事件发生或经过指定时间后唤醒进程。

示例– 内核仅在这些条件之一发生时才会返回

  • {1, 2, 3} 中的任何描述符都可以读取
  • {4, 5, 6} 中的任何描述符都已准备好写入
  • 时间5秒过去了

整个过程可以分解为以下步骤:

服务器:

  1. 创建 TCP 即监听套接字
  2. 创建一个 UDP 套接字
  3. 将两个套接字绑定到服务器地址。
  4. 为 select 初始化一个描述符集并计算我们将等待的最多 2 个描述符
  5. 调用 select 并获取准备好的描述符(TCP 或 UDP)
  6. 如果就绪描述符是 TCP,则处理新连接;如果就绪描述符是 UDP,则接收数据报

UDP客户端:

  1. 创建一个 UDP 套接字。
  2. 向服务器发送消息。
  3. 等到收到来自服务器的响应。
  4. 关闭套接字描述符并退出。

TCP客户端:

  1. 创建一个 TCP 套接字。
  2. 调用 connect 与服务器建立连接。
  3. 当连接被接受时,向服务器写入一条消息。
  4. 读取服务器的响应。
  5. 关闭套接字描述符并退出。

必要功能:

int select(int maxfd, fd_set *readsset, fd_set *writeset, 
fd_set *exceptset, const struct timeval *timeout);
Returns: positive count of descriptors ready, 0 on timeout, -1 error

论据:

  • maxfd:准备好的描述符的最大数量。
  • timeout:等待选择返回的时间。
struct timeval{
long tv_sec;
long tv_usec;
};
if timeout==NULL then wait forever
if timeout == fixed_amount_time then wait until specified time
if timeout == 0 return immediately.
  • readset:我们希望内核测试读取的描述符集。
  • writeset:我们希望内核测试写入的描述符集。
  • exceptset:我们希望内核测试异常条件的描述符集。
int read(int sockfd, void * buff, size_t nbytes);
Returns:  number of bytes read from the descriptor. -1 on error

论据:

  1. sockfd:接收数据的描述符。
  2. buff:将应用程序缓冲区套接字描述符数据复制到此缓冲区。
  3. nbytes:要复制到应用程序缓冲区的字节数。

服务器.c

C
// Server program
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define PORT 5000
#define MAXLINE 1024
int max(int x, int y)
{
    if (x > y)
        return x;
    else
        return y;
}
int main()
{
    int listenfd, connfd, udpfd, nready, maxfdp1;
    char buffer[MAXLINE];
    pid_t childpid;
    fd_set rset;
    ssize_t n;
    socklen_t len;
    const int on = 1;
    struct sockaddr_in cliaddr, servaddr;
    char* message = "Hello Client";
    void sig_chld(int);
 
    /* create listening TCP socket */
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);
 
    // binding server addr structure to listenfd
    bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    listen(listenfd, 10);
 
    /* create UDP socket */
    udpfd = socket(AF_INET, SOCK_DGRAM, 0);
    // binding server addr structure to udp sockfd
    bind(udpfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
 
    // clear the descriptor set
    FD_ZERO(&rset);
 
    // get maxfd
    maxfdp1 = max(listenfd, udpfd) + 1;
    for (;;) {
 
        // set listenfd and udpfd in readset
        FD_SET(listenfd, &rset);
        FD_SET(udpfd, &rset);
 
        // select the ready descriptor
        nready = select(maxfdp1, &rset, NULL, NULL, NULL);
 
        // if tcp socket is readable then handle
        // it by accepting the connection
        if (FD_ISSET(listenfd, &rset)) {
            len = sizeof(cliaddr);
            connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &len);
            if ((childpid = fork()) == 0) {
                close(listenfd);
                bzero(buffer, sizeof(buffer));
                printf("Message From TCP client: ");
                read(connfd, buffer, sizeof(buffer));
                puts(buffer);
                write(connfd, (const char*)message, sizeof(buffer));
                close(connfd);
                exit(0);
            }
            close(connfd);
        }
        // if udp socket is readable receive the message.
        if (FD_ISSET(udpfd, &rset)) {
            len = sizeof(cliaddr);
            bzero(buffer, sizeof(buffer));
            printf("\nMessage from UDP client: ");
            n = recvfrom(udpfd, buffer, sizeof(buffer), 0,
                        (struct sockaddr*)&cliaddr, &len);
            puts(buffer);
            sendto(udpfd, (const char*)message, sizeof(buffer), 0,
                (struct sockaddr*)&cliaddr, sizeof(cliaddr));
        }
    }
}


C
// TCP Client program
#include 
#include 
#include 
#include 
#include 
#include 
#define PORT 5000
#define MAXLINE 1024
int main()
{
    int sockfd;
    char buffer[MAXLINE];
    char* message = "Hello Server";
    struct sockaddr_in servaddr;
 
    int n, len;
    // Creating socket file descriptor
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket creation failed");
        exit(0);
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
 
    // Filling server information
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
    if (connect(sockfd, (struct sockaddr*)&servaddr,
                            sizeof(servaddr)) < 0) {
        printf("\n Error : Connect Failed \n");
    }
 
    memset(buffer, 0, sizeof(buffer));
    strcpy(buffer, "Hello Server");
    write(sockfd, buffer, sizeof(buffer));
    printf("Message from server: ");
    read(sockfd, buffer, sizeof(buffer));
    puts(buffer);
    close(sockfd);
}


C
// UDP client program
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define PORT 5000
#define MAXLINE 1024
int main()
{
    int sockfd;
    char buffer[MAXLINE];
    char* message = "Hello Server";
    struct sockaddr_in servaddr;
 
    int n, len;
    // Creating socket file descriptor
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("socket creation failed");
        exit(0);
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
 
    // Filling server information
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    // send hello message to server
    sendto(sockfd, (const char*)message, strlen(message),
        0, (const struct sockaddr*)&servaddr,
        sizeof(servaddr));
 
    // receive server's response
    printf("Message from server: ");
    n = recvfrom(sockfd, (char*)buffer, MAXLINE,
                0, (struct sockaddr*)&servaddr,
                &len);
    puts(buffer);
    close(sockfd);
    return 0;
}


TCP_Client.c

C

// TCP Client program
#include 
#include 
#include 
#include 
#include 
#include 
#define PORT 5000
#define MAXLINE 1024
int main()
{
    int sockfd;
    char buffer[MAXLINE];
    char* message = "Hello Server";
    struct sockaddr_in servaddr;
 
    int n, len;
    // Creating socket file descriptor
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket creation failed");
        exit(0);
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
 
    // Filling server information
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
    if (connect(sockfd, (struct sockaddr*)&servaddr,
                            sizeof(servaddr)) < 0) {
        printf("\n Error : Connect Failed \n");
    }
 
    memset(buffer, 0, sizeof(buffer));
    strcpy(buffer, "Hello Server");
    write(sockfd, buffer, sizeof(buffer));
    printf("Message from server: ");
    read(sockfd, buffer, sizeof(buffer));
    puts(buffer);
    close(sockfd);
}

UDP_client.c

C

// UDP client program
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define PORT 5000
#define MAXLINE 1024
int main()
{
    int sockfd;
    char buffer[MAXLINE];
    char* message = "Hello Server";
    struct sockaddr_in servaddr;
 
    int n, len;
    // Creating socket file descriptor
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("socket creation failed");
        exit(0);
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
 
    // Filling server information
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    // send hello message to server
    sendto(sockfd, (const char*)message, strlen(message),
        0, (const struct sockaddr*)&servaddr,
        sizeof(servaddr));
 
    // receive server's response
    printf("Message from server: ");
    n = recvfrom(sockfd, (char*)buffer, MAXLINE,
                0, (struct sockaddr*)&servaddr,
                &len);
    puts(buffer);
    close(sockfd);
    return 0;
}

编译运行上述代码的步骤:

  1. 编译服务器程序(gcc server.c -o ser)
  2. 使用 (./ser) 运行服务器
  3. 在另一个终端上,编译 tcp 客户端程序(gcc tcp_client.c -o tcpcli)
  4. 运行 tcp 客户端 (./tcpcli)
  5. 在另一个终端上,编译 udp 客户端程序(gcc udp_client.c -o udpcli)
  6. 运行 udp 客户端 (./udpcli)

上述代码的输出: