【问题标题】:UDP Server child process can't sendUDP Server 子进程无法发送
【发布时间】:2013-09-07 15:17:49
【问题描述】:

我正在尝试使用 UDP 创建聊天程序。我在客户端和服务器中都创建了一个父进程和一个子进程来分别接收和发送消息。问题是服务器无法发送。请帮忙。这是我的代码。

客户

    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/stat.h>
    #include <arpa/inet.h>

    int main(int argc, char **argv)
    {
        int sockfd;
        struct sockaddr_in servaddr;
        socklen_t len = sizeof(servaddr);
        char mesg[1024], rmesg[1024];
        pid_t pid;

        if(argc!=2){ 
            printf("Usage: %s <ip_addr>\n",argv[0]);
            exit(1);
        }

        sockfd = socket(PF_INET,SOCK_DGRAM,0);
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(54321);
        inet_pton(AF_INET,argv[1],&servaddr.sin_addr);

        pid = fork();
        if(pid == 0) {
            printf("Type 'exit' to Exit. \n");
            while(1){
                fgets(mesg,sizeof(mesg),stdin);
                sendto(sockfd,mesg,strlen(mesg),0,(const struct sockaddr *)&servaddr,len);
                if(strcmp(mesg, "exit\n") == 0)
                    break;
            }
            close(sockfd);
            kill(pid, SIGINT);
            exit(0);
        }else{
            while(1){
                memset(rmesg,0,sizeof(rmesg));
                if(recv(sockfd,rmesg,sizeof(rmesg),0) > 0 ){
                    printf("From Server: %s", rmesg);
                }
            }
        }
        close(sockfd);
        return 0;
    }

服务器

    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/stat.h>
    #include <arpa/inet.h>

    int main(int argc, char **argv)
    {
        int sockfd1;
        struct sockaddr_in servaddr,cliaddr;
        socklen_t len = sizeof(cliaddr);
        char cli_ip[32];
        char mesg[1024], smesg[1024];
            pid_t  pid1;

        sockfd1 = socket(PF_INET,SOCK_DGRAM,0);
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(54321);
        inet_ntop(AF_INET,(struct in_addr *) &cliaddr.sin_addr, cli_ip, sizeof(cli_ip) );

        if ( bind( sockfd1, (struct sockaddr*) &servaddr, sizeof(servaddr) ) < 0 ){
            perror(NULL);
            exit(-1);
        }

        pid1 = fork();
        if(pid1 == 0){
            while(1){
                memset(mesg,0,sizeof(mesg));
                if( recvfrom(sockfd1,mesg,sizeof(mesg),0,(struct sockaddr *)&cliaddr,&len) > 0 ){
                    printf("From client: %s",mesg);
                }
            }
        }else{
            printf("Type 'exit' to Exit. \n");
            while(1){
                fgets(smesg,sizeof(smesg),stdin);
                sendto(sockfd1,smesg,strlen(smesg),0,(struct sockaddr *)&cliaddr,len);
                if(strcmp(smesg, "exit\n") == 0)
                    break;
            }
            close(sockfd1);
            kill(pid1, SIGINT);
            exit(0);
        }
        return 0;
    }

【问题讨论】:

  • 这段代码完全没有对读写和大多数其他系统调用的任何错误检查!首先这是不好的和不明智的,因为检测错误是对它们做出反应的唯一可能性。而且至少在开发、测试和调试过程中帮助很大!
  • 为什么服务器在一个未初始化的cliaddr.sin_addr上调用inet_ntop?它还在子进程中调用recvfrom(),但父进程试图在其sendto 中使用cliaddr——父进程中的结构未更新。

标签: c linux


【解决方案1】:

问题很可能出在客户端:

while(1){
    if(recv(sockfd,rmesg,sizeof(rmesg),0) > 0 ){
        printf("From Server: %s", rmesg);
    }
}

您正在一个未连接的套接字上调用 recv。您可能会收到您忽略的错误(可能是ENOTCONN)。而且我相信您还会看到客户端的 CPU 使用率很高,因为它不断循环,每次系统调用都失败。

解决方案是改用recvfrom。或者,您可以在套接字上调用connect,即使它是SOCK_DGRAM,但通常不会这样做。

【讨论】:

  • 如果不调用connect(),则必须调用bind()设置本地端口监听。
  • @Barmar,您不需要为 UDP 连接套接字。只要它绑定到发送方发送数据的端口,接收方就应该能够接收到它。当然,UDP 套接字也可以调用 connect(),正如您所提到的。
  • @ManojPandey 这不是我说的吗?您似乎在评论答案,而不是我的评论。
  • 同意 (+1)。谢谢。我的评论是针对@cnicutar。
  • @ManojPandey 我从来没有说过你需要为 UDP 连接它。如果你想直接使用recv,你需要连接它。
【解决方案2】:

为什么你为服务器和客户端使用相同的套接字?这根本不符合逻辑。一个更简单的选择是使用两个线程。一个线程处理服务器套接字,另一个线程处理客户端线程。当然,您还需要有两个套接字——一个用于服务器,一个用于客户端。另外,sendto() 需要有接收者的地址和端口信息。

【讨论】:

  • @user2757209 如果您分别为客户端和服务器运行代码,为什么要同时为客户端/服务器执行 fork()?你应该使用线程来处理这个——它们更有效。为每个客户创建一个流程是非常低效的。
  • 我还不知道如何使用线程,所以我选择了fork()。效率并不重要,这只是一个双向通信程序..
  • 对于当前选项,您仍然可以使用主进程来完成您的任务,因为 sendto() 是一个非阻塞调用。如果您需要在某个时候进行扩展,那么我强烈建议您花一些时间在 Pthreads 上。创建进程比创建线程要昂贵得多。花费时间是值得的,尤其是在您尝试扩展时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-16
  • 1970-01-01
相关资源
最近更新 更多