【问题标题】:Unix Sockets: Client stops receiving messages correctly after first messageUnix套接字:客户端在第一条消息后停止正确接收消息
【发布时间】:2014-10-04 06:52:04
【问题描述】:

信息

[root@xilinx Downloads]# uname -a
Linux xilinx 2.6.32-71.el6.x86_64 #1 SMP Wed Sep 1 01:33:01 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux

我正在尝试使用 Unix 套接字制作一个简单的聊天应用程序:

编译代码后,我首先在一个终端上运行 server,它说,等待连接。然后我在同一台计算机上的另一个终端上启动 client,它连接成功。然后客户端发送第一条消息。服务器收到它非常好。

但是在那之后发生了一些奇怪的事情。我必须输入几次才能使其正常工作并获得整个消息。

这是一个简单的运行输出:

服务器端

[root@xilinx Downloads]# ./server 
Waiting for a connection...
Connected.

客户端

[root@xilinx Downloads]# 
[root@xilinx Downloads]# 
[root@xilinx Downloads]# ./client 
Trying to connect...
Connected.

SingleWord //客户端将此消息发送到服务器

服务器端

>received<SingleWord  //server receives this- good.  
IamServer             // Now server sends some message 

客户端 ///我必须按几次回车才能得到整个消息

echo> Ia> 
echo> mS> 
echo> er> 
echo> ve> 
echo> r

请帮我解决这个问题:

代码如下。

Server.c

#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/un.h> 
#define SOCK_PATH "echo_socket"

int main(void) {
    int s, s2, t, len;
    struct sockaddr_un local, remote;
    char str[100];
    char str2[100];
    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }
    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, SOCK_PATH);
    unlink(local.sun_path);
    len = strlen(local.sun_path) + sizeof(local.sun_family);
    if (bind(s, (struct sockaddr * ) & local, len) == -1) {
        perror("bind");
        exit(1);
    }
    if (listen(s, 5) == -1) {
        perror("listen");
        exit(1);
    }
    for (;;) {
        int done, n;
        printf("Waiting for a connection...\n");
        t = sizeof(remote);
        if ((s2 = accept(s, (struct sockaddr * ) & remote, & t)) == -1) {
            perror("accept");
            exit(1);
        }
        printf("Connected.\n");
        done = 0;
        do {
            while (1) {
                n = recv(s2, str, 100, 0);
                printf(">received<%s", str);
                if (n <= 0) {
                    if (n < 0) perror("recv");
                    printf("error");
                    done = 1;
                }
                //m = fgets(str2,100,stdin);
                if (!done)
                    while (fgets(str2, 100, stdin) > 0) {
                        if (send(s2, str2, strlen(str2), 0) < 0) {
                            perror("send");
                            //if (send(s2, str2, m, 0) < 0) {
                            //perror("send");
                            done = 1;
                        }
                    }
            }
        } while (!done);
        close(s2);
    }
    return (0);
}

Client.c

#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/un.h> 
#define SOCK_PATH "echo_socket"

int main(void) {
    int s, t, len;
    struct sockaddr_un remote;
    char str[100];
    char str2[100];
    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }
    printf("Trying to connect...\n");
    remote.sun_family = AF_UNIX;
    strcpy(remote.sun_path, SOCK_PATH);
    len = strlen(remote.sun_path) + sizeof(remote.sun_family);
    if (connect(s, (struct sockaddr * ) & remote, len) == -1) {
        perror("connect");
        exit(1);
    }
    printf("Connected.\n");
    while (1) {
        while (printf("> "), fgets(str, 100, stdin), !feof(stdin)) {
            if (send(s, str, strlen(str), 0) < 0) {
                perror("send");
                exit(1);
            }
            if ((t = recv(s, str2, strlen(str2), 0)) > 0) {
                str2[t] = '\0';
                printf("echo> %s", str2);
            } else {
                if (t < 0) perror("recv");
                else printf("Server closed connection\n");
                exit(1);
            }
        }
    }
    close(s);
    return 0;
}

【问题讨论】:

  • 缩进会很好,不是每个人都足够聪明和流利地使用 C 套接字编程来编写或读取没有缩进的代码。
  • 没有深入研究,我假设问题与阻塞 IO 有关,因为您没有任何努力切换到非阻塞。
  • 这一行: if ((t = recv(s, str2, strlen(str2), 0)) > 0) { 试图获取数组 str2 中字符串的长度,但是,有在 recv() 完成执行之前,没有任何东西放入该数组中。

标签: c linux sockets unix


【解决方案1】:

您的代码中有一些小问题:

  • server.c:

    • n = recv(s2, str, 100, 0); 之后,在使用它之前不要为空终止 str - 你应该有:

      n = recv(s2, str, 100, 0);
      str[(n > 0) ? n : 0] = '\0';
      
    • while(1) 应该是 while (! done),如果出现错误,则会出现无限循环显示错误 - 你应该有:

      do {
          while (! done) {
      
  • client.c:

    • 您在recv 中使用strlen t = recv(s, str2, strlen(str2), 0) - 应该是:

      if ((t = recv(s, str2, sizeof(str2), 0)) > 0) {
      
    • while (1) { while (printf("&gt; "), fgets(str, 100, stdin), !feof(stdin)) 不符合您的预期,如果在标准输入上使用 eof,则外循环继续 - 您可以编写(前提是上面声明了 int done = 0):

      while (! done) {
          done = 1;
          while (printf("> "), fgets(str, 100, stdin) != NULL) {
              done = 0;
      

而且我不保证没有其他同类型的小问题,你应该用select做IO复用,因为现在客户端和服务器必须轮流交谈。

【讨论】:

  • +1。您还应该注意,流套接字不保证消息作为完整单元发送。
  • 我同意在 recv() 之前使用 select() 函数,所有这些都在循环中,直到出现 select() 超时。在尝试打印之前,您需要添加一些方法来确定是否已收到完整消息。 select() 超时将是确定是否收到完整消息的简单方法。这也意味着每次成功执行 recv() 时,读取的字符都应该连接到已经读取的字符。此外,每个缓冲区都应在每次使用前初始化为全 '\0'。
  • @user3629249 @kmkaplan:我同意你们两个:这些程序远非专业级;-)。我只是展示了主要问题的原因。只有一个精度,代码现在在收到最后一个字符后添加了一个\0,所以我不确定缓冲区是否真的必须初始化为空 - 即使这是一种很好的做法。
【解决方案2】:

您可能需要在客户端启动一个新线程来接收来自服务器的消息或使用 I/O 多路复用。

【讨论】:

  • 我在使用两个不同的终端,所以它关联了两个不同的线程(我错了吗?),那为什么我需要创建一个新线程?
猜你喜欢
  • 1970-01-01
  • 2021-04-07
  • 2017-02-13
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 2015-03-28
  • 1970-01-01
相关资源
最近更新 更多