【问题标题】:Ruby Client cannot read from C Server: socket.gets hangsRuby 客户端无法从 C 服务器读取:socket.gets 挂起
【发布时间】:2014-08-13 19:42:43
【问题描述】:

我有一个 C 语言服务器和一个 Ruby 客户端。我的 ruby​​ 客户端能够成功地向我的 C 服务器发送一个字符串,但它无法从它接收任何输入。

更具体地说,我在 client.rb 中的“recv”方法中的“gets”方法挂起。启动我的 client.rb 程序如下所示:

[user]$ ruby client.rb
## CLIENT STARTED ##
user: myuser       //user enters username and password here
password: mypass   // It hangs after this line, so I press ctrl-C

^Cclient.rb:27:in `gets': Interrupt     //This is the output after pressing ctrl-C
from client.rb:27:in `verifyUser'
from client.rb:40:in `main'
from client.rb:48

而我的服务器的输出是:

## SERVER STARTED ##
[Wed Aug 13 12:19:43 2014]  21 bytes  0 myuser mypass
write succeed
request processed

根据我的输出,写入客户端成功。但是当在 recv 方法中调用 socket.gets 时,我的 ruby​​ 客户端挂起。我也尝试过使用 socket.read 并且也挂起。我只需要从我的服务器获取第一个字节或一行输入。

为什么我的客户端不能从我的服务器接收输入?

应该发生的事情的清单是:

  • 服务器已启动
  • 客户端启动,用户输入用户名和密码
  • 服务器收到用户名和密码
  • 服务器向客户端发送字符串
  • 客户端从服务器接收字符串
  • 客户端根据字符串做一些操作

client.rb

$ip = '127.0.0.1'
$port = 29008

def send(msg)
  socket = TCPSocket.new($ip, $port)
  socket.write(msg)
  socket.close
end

def recv
  socket = TCPSocket.new($ip, $port)
  data = socket.gets
  socket.close
  data
end

def verifyUser(user, pw)
  send("0 #{user.chomp!} #{pw.chomp!}")
  recv =~ /^0/
end


def main

  puts "## CLIENT STARTED ##"
  print "user: "
  user = gets
  print "password: "
  pw = gets

  if verifyUser(user, pw) then
    puts "user verified"
  else
    puts "wrong user or password"
  end

end

main

server.c

/* PROCESS REQUEST */
void processRequest(char *input, int cfd) {

  int option = 0;
  char *ch = "0";

  if (option == 0) {

     if (send(cfd, ch, strlen(ch), 0) < 0) {
        puts ("write error");
     } else {
        puts ("write succeed");
     }

  } else {
     puts("option is not valid");
  }
}



int main() {

  int sfd, cfd;
  int optval = 1;
  char buffer[2000];
  char input[2000];
  int len;
  int idx = 0;
  time_t currTime;
  char *time_str;


  struct sockaddr_in saddr, caddr;


  sfd = socket(AF_INET, SOCK_STREAM, 0);


  saddr.sin_family = AF_INET; /* Set address family to IPv4 Internet */
  saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any internet address */
  saddr.sin_port = htons(29008); /* Set server port to 29008 */

 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);

  bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));


  listen(sfd, 1);

  printf("## SERVER STARTED ##\n");

  while (1) {



        if ((cfd = accept(sfd, (struct sockaddr *)NULL, NULL)) < 0) {
                puts("client connection failed");
        }

        /* get time of message */
        currTime = time(NULL);
        time_str = asctime(localtime(&currTime));
        time_str[strlen(time_str) - 1] = '\0';


        /* ensure that all everything is received from client */
        while ((len = recv(cfd, buffer, 2000, 0)) > 0) {

                buffer[len] = '\0';

                if (idx == 0) {
                   strcpy(input, buffer);
                } else {
                   strcat(input, buffer);
                }

                /* log message */
                printf ("[%s]  %d bytes  %s\n", time_str, len, input);

                idx++;
        }

        if (len < 0) {
                printf("recieve failed\n");
                return 1;
        }

        processRequest(input,cfd);
        puts("request processed");


        close(cfd);

        idx = 0;
        input[0] = '\0';
  }

  return 0;
}

编辑

  • 我已经修改了我的 server.c 代码以反映我现在所拥有的内容。我将 send 方法中的 strlen(input) 参数更改为 strlen(ch)。问题仍然存在。

  • 我已经通过 telnet 测试了我的服务器,结果是一样的。我可以写入我的服务器,但不能从中读取。这让我相信我的 Ruby 客户端没有任何问题。这一定是我的服务器发送数据的方式有问题。

【问题讨论】:

    标签: c ruby sockets networking client-server


    【解决方案1】:

    修改后的答案:

    1) 在 ruby​​ 代码中,您没有使用同一个套接字来从服务器写入和读取。在 verifyUser 中创建套接字并将其传递给 send/recv - 这样您就可以保持与服务器的连接。

    def send(s, msg)
      print("sending: '" + msg + "'\n")
      s.write(msg)
      s.flush
    end
    
    def recv(s)
      print("in recv");
      data = s.recv(1024)
      print("got data: " + data);
      data   
    end
    
    def verifyUser(user, pw)
      socket = TCPSocket.new($ip, $port)
      send(socket,"0 #{user.chomp!} #{pw.chomp!}\0")
      resp = recv(socket) 
      socket.close
      resp =~ /^0/
    end
    

    2) 在 c 服务器中 - len = recv while 循环 - 你真的需要 while 循环吗?服务器再次读取命令和循环。它之前工作的唯一原因是因为您关闭了套接字,然后 recv 得到错误,弹出循环并处理请求。如果您将 processRequest 放在带有时间戳的 printf 正下方,您会看到服务器确实发送了响应。如果你整理了 ruby​​ 客户端套接字的东西,你实际上会得到那个响应。

    【讨论】:

    • 抱歉,我编辑了 processRequest 方法,忘记取出多余的参数。我已经根据你所说的更新了我的 server.c 代码。它编译。我的 server.c 代码到达“写入成功”和“请求处理”打印语句,所以我相信服务器端能够成功发送字符串。客户端仍然挂在“socket.gets”
    • 是的,服务器端首先获得了连接。我在上面发布了客户端连接时服务器的输出。本质上我可以让客户端成功地向服务器发送一个字符串,但我不能让它从服务器读取。
    • 请注意,我修改了上面的评论。
    【解决方案2】:
    1. Ruby:send() 和 recv() 应该都使用同一个套接字,而不是每个都使用不同的新套接字。
    2. Ruby:gets() 依赖于换行符来了解输入何时完成。
    3. C:与 #2 类似,您的 recv() 正在等待 2000 个字节来结束循环,而不是换行符或其他任何内容。

    Ruby 代码:

    def send(socket, msg)
      socket.puts(msg) # puts() adds a newline for the server.
      socket.flush     # Probably not necessary.
    end
    
    def recv(socket)
      # gets() doesn't end until receives a newline.
      socket.gets
    end
    
    def verifyUser(user, pw)
      response = ''
      
      # Use 1 socket for both send/recv.
      # Can use open() with a block to auto-close
      #   at the end of the block.
      TCPSocket.open($ip, $port) do |socket|
        send(socket,"0 #{user.chomp!} #{pw.chomp!}")
        response = recv(socket)
      end
      
      response =~ /^0/
    end
    

    C 代码:

    void processRequest(char *input, int cfd) {
      int option = 0;
      char *ch = "0\n"; // Newline!
    
      //...
    }
    
    //...
    
            /* ensure that all everything is received from client */
            while ((len = recv(cfd, buffer, 2000, 0)) > 0) {
    
                    buffer[len] = '\0';
                    
                    if (idx == 0) {
                       strcpy(input, buffer);
                    } else {
                       strcat(input, buffer);
                    }
    
                    /* log message */
                    printf ("[%s]  %d bytes  %s\n", time_str, len, input);
    
                    idx++;
                    
                    // Note that this is just a simple version.
                    // You probably want to store a substring into input.
                    // Then save the rest of the substring in the buffer.
                    // Include "stdbool.h" if don't want to use an int.
                    int has_newline = 0;
                    
                    for(int i = 0; i < len; ++i) {
                      if(buffer[i] == '\n') {
                        printf("Breaking out of loop.\n");
                        has_newline = 1;
                        break;
                      }
                    }
                    
                    if(has_newline == 1) {
                      break;
                    }
            }
    
    //...
    

    【讨论】:

      猜你喜欢
      • 2017-09-13
      • 2021-09-05
      • 1970-01-01
      • 2018-05-19
      • 1970-01-01
      • 2017-04-30
      • 2012-05-26
      • 2015-06-21
      • 1970-01-01
      相关资源
      最近更新 更多