qq_33477173 2024-09-09 23:41 采纳率: 100%
浏览 2
已结题

linux的udp服务器select模式客户端和服务器无法通信,如何解决?

请问“linux的udp服务器select模式客户端和服务器无法通信”,如何解决

第一次学这段代码,有些不会的地方,请各位帮忙改正代码,感恩

问题

  1. 服务器无法接收客户端发送的数据
  2. 客户端一直显示Timeout

运行结果

服务器运行结果:

img

客户端运行结果:

img

服务器

代码如下

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/wait.h>

#define PORT 5001
#define Server_IP_ADDR "192.168.204.136"
#define BACKLOG 10
#define QUIT_CMD "quit"
#define SERV_RESP_STR "Server :"

void *handle_client_msg(void *arg) {
    int new_fd = *(int *) arg;
    printf("New client %d connected.\n", new_fd);

    char buffer[BUFSIZ];
    char resp_buffer[BUFSIZ + 10];
    int ret;

    while (1) {
        bzero(buffer, BUFSIZ);
        do {
            ret = read(new_fd, buffer, BUFSIZ - 1);
        } while (ret < 0 && errno == EINTR);

        if (ret < 0) {
            perror("read");
            break;
        }

        if (!ret) {
            printf("Client process has closed the connection.\n");
            break;
        }

        printf("Received: %s\n", buffer);

        bzero(resp_buffer, sizeof(resp_buffer));
        snprintf(resp_buffer, sizeof(resp_buffer), "%s%s", SERV_RESP_STR, buffer);
        do {
            ret = write(new_fd, resp_buffer, strlen(resp_buffer));
        } while (ret < 0 && errno == EINTR);

        if (ret < 0) {
            perror("write");
            break;
        }

        if (strlen(buffer) > strlen(SERV_RESP_STR) &&
            !strncasecmp(buffer + strlen(SERV_RESP_STR), QUIT_CMD, strlen(QUIT_CMD))) {
            printf("Client process %d has quit.\n", new_fd);
            break;
        }
    }

    close(new_fd);
    free(arg);
    return NULL;
}

void sig_child_handler(int signo) {
    if (signo == SIGCHLD) {
        while (waitpid(-1, NULL, WNOHANG) > 0);
    }
}

int main(int argc, const char *argv[]) {
    int sockfd;
    struct sockaddr_in server_addr;

    signal(SIGCHLD, sig_child_handler);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }

    int optval = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
        perror("setsockopt");
        exit(1);
    }

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        exit(1);
    }

    if (listen(sockfd, BACKLOG) < 0) {
        perror("listen");
        exit(1);
    }

    printf("Server is Started.\n");

    int new_fd;
    pid_t pid;
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);

    while (1) {
        new_fd = accept(sockfd, (struct sockaddr *) &client_addr, &client_addr_len);
        if (new_fd < 0) {
            perror("accept");
            continue;
        }

        if ((pid = fork()) < 0) {
            perror("fork");
            close(new_fd);
        } else if (pid == 0) {
            close(sockfd);
            char client_ip[INET_ADDRSTRLEN];
            if (!inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN)) {
                perror("inet_ntop");
                exit(1);
            }
            printf("Client IP: %s:%d\n", client_ip, ntohs(client_addr.sin_port));
            handle_client_msg((void *) (intptr_t) new_fd);

            exit(0);
        } else {
            close(new_fd);
        }
    }

    close(sockfd);
    return 0;
}

客户端

代码如下
补充说明:运行时需要三个参数:运行文件 服务器地址 服务器端口

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>

#define PORT 5001
#define Server_IP_ADDR "192.168.204.136"
#define BACKLOG 10
#define QUIT_CMD "quit"
#define SERV_RESP_STR "Server :"

void usage(const char *prog_name) {
    printf("%s Usage: server_ip_addr port \n", prog_name);
    printf("server_ip_addr: %s\n", Server_IP_ADDR);
    printf("port: %d (>5000) \n\n", PORT);
}

int main(int argc, const char *argv[]) {
    int sockfd = -1;
    struct sockaddr_in server_addr;
    int port = PORT;

    if (argc != 3) {
        usage(argv[0]);
        exit(1);
    }

    port = atoi(argv[2]);
    if (port <= 5000) {
        usage(argv[0]);
        exit(1);
    }

    // 创建socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return 0;
    }

    // 准备服务器地址
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    if (inet_pton(AF_INET, argv[1], &server_addr.sin_addr) != 1) {
        perror("inet_pton");
        close(sockfd); // 关闭套接字
        exit(1);
    }

    printf("client is running...\n");

    //非阻塞模式
    fd_set rfds;
    int maxfd = -1;
    struct timeval timeout;
    char buffer[BUFSIZ];
    int result = -1;

    while (1) {
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        FD_SET(sockfd, &rfds);
        maxfd = sockfd > 0 ? sockfd : 0;

        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        int select_result = select(maxfd + 1, &rfds, NULL, NULL, &timeout);
        if (select_result < 0) {
            perror("select");
            close(sockfd);
            break;
        } else if (select_result == 0) {
            printf("Timeout, no data received.\n");
            continue;
        }

        if (FD_ISSET(0, &rfds)) {// 标准键盘有输入
            // 读取键盘输入至网络套接字fd
            bzero(buffer, sizeof(buffer));
            do {
                result = read(0, buffer, BUFSIZ - 1);
            } while (result < 0 && errno == EINTR);
            if (result < 0) {
                perror("read");
                close(sockfd);
                break;
            }
            if (!result) continue; // 未读取到数据
            if (sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
                perror("sendto");
                close(sockfd);
                break;
            }
            if (!strncasecmp(buffer, QUIT_CMD, strlen(QUIT_CMD))) {
                printf("quit command received, exit...\n");
                break;
            }
        }

        if (FD_ISSET(sockfd, &rfds)) {// 服务器发送过来数据
            // 读取网络套接字fd至标准输出
            bzero(buffer, sizeof(buffer));
            socklen_t addr_len = sizeof(server_addr);
            do {
                result = recvfrom(sockfd, buffer, BUFSIZ - 1, 0, (struct sockaddr *) &server_addr, &addr_len);
            } while (result < 0 && errno == EINTR);
            if (result < 0) {
                perror("recvfrom");
                continue;
            }
            if (!result) break; // 服务器关闭连接
            printf("server: %s\n", buffer);
            if (!strncasecmp(buffer, QUIT_CMD, strlen(QUIT_CMD))) {
                printf("quit command received, exit...\n");
                break;
            }
        }
    }

    // 关闭套接字
    close(sockfd);
    return 0;
}
  • 写回答

2条回答 默认 最新

  • qq_33477173 2024-09-10 11:24
    关注

    已解决

    1. 创建socket时未使用udp模式
      sockfd = socket(AF_INET, SOCK_DGRAM, 0);
      
    2. 修改select结果判断,删除elseif部分
           if (select_result < 0) {
               perror("select");
               close(sockfd);
               break;
           }
      
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 9月18日
  • 已采纳回答 9月10日
  • 创建了问题 9月9日

悬赏问题

  • ¥15 35114 SVAC视频验签的问题
  • ¥15 impedancepy
  • ¥15 在虚拟机环境下完成以下,要求截图!
  • ¥15 求往届大挑得奖作品(ppt…)
  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图
  • ¥15 UE5.1局部变量对蓝图不可见