输出到连接请求不是原子的

时间:2012-04-21 15:04:43

标签: c sockets connect

我创建了一个套接字,我正在尝试接受连接。一切正常。但是,输出让我对以下代码的工作原理感到困惑。

      // this a server program
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/un.h>
    #include <arpa/inet.h>
    #include <errno.h>
    #include <wait.h>

    #define LISTENQ (1024)

    int main(void) {

       int lstn_sock, conn_sock;
       struct sockaddr_in my_serv;
       short int pnum = 4080;

       // create listening socket
       if( (lstn_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("Error (socket): %s\n",strerror(errno));
        exit(1);
       }

       // initialize socket address
       memset( &my_serv, 0, sizeof(my_serv) );
       my_serv.sin_family = AF_INET;
       my_serv.sin_addr.s_addr = INADDR_ANY;
       my_serv.sin_port = htons(pnum);

       // associate address with socket. 
       if( bind(lstn_sock, (struct sockaddr *) &my_serv, sizeof(my_serv)) < 0){
        printf("Error (bind): %s\n",strerror(errno));
        exit(1);
       }
        //printf("lstn_sock: %d\n",lstn_sock);
       // start listening to socket
       if( listen(lstn_sock, LISTENQ) < 0){
        printf("Error (listen): %s\n",strerror(errno));
        exit(1);
       }

       // make it a daemon
       while(1){
        // retrieve connect request and connect
        if( (conn_sock = accept(lstn_sock, NULL, NULL)) < 0){
           printf("Error (accept): %s\n",strerror(errno));
           exit(1);
        }

        printf("The server says hi!\n");

        // close connected socket
        if( close(conn_sock) < 0){
           printf("Error (close): %s\n",strerror(errno));
           exit(1);
        }
       }

    return 0;

    }

@ubuntu:$。/ my_code&amp; @ubuntu:$ telnet localhost 4080

以下是上述代码的两个不同输出:
输出1
尝试:: 1 ...
尝试127.0.0.1 ..
连接到localhost。
逃脱角色是'^]' 服务员说嗨!
外部主机关闭连接。

输出2
尝试:: 1 ...
尝试127.0.0.1 ..
服务员说嗨!
连接到localhost。
逃脱角色是'^]' 外部主机关闭连接。

有人可以解释一下“服务员说嗨!”的动作原因 在输出中。

2 个答案:

答案 0 :(得分:2)

我猜你在后台启动了你的服务器,并且在同一个终端中使用telnet来测试它。

您的服务器将该问候语打印到标准输出,telnet也会写入标准输出。它们是两个独立的进程,可以独立调度(可能同时进行)。您无法预测输出在终端上的确切顺序。

答案 1 :(得分:1)

我写了一个更长的答案,但后来内核恐慌!!!!非常讨厌我的MOBO。但这是一个较短的重新上限。

我看到你的问题已经解决,但只是要明确:

你可以read - 和潜艇。

您正在使用终端。终端通常有三个streams

  • 标准输入
  • 标准输出
  • stderr的

当您启动服务器程序时,将在该终端中启动一个进程。

该进程从终端进程继承了各种“ things ”,如环境变量,限制等。服务器也成为shell的进程。 shell本身通常是更高进程的子级,依此类推。

当你使用&符时,进程被放入后台,但仍然是同一终端的子进程。

对任一流的写入都将作为启动该进程的shell写入 相同的流

在典型的服务器/客户端方案中,您不会写入stdout来发送消息。服务器的stdout绑定到服务器环境(/ proc / [pid] / fd / ...)。客户端通常在不同的系统上(通常)无法访问服务器上的任何内容 - 因此是套接字的整个概念 - 独立系统之间的通信。这就是你写连接的原因。


使用各种Linux工具快速查看您的案例;

如果您发出pstree,您可以轻松地将其显示出来;像(删除了很​​多):

$ ./server 4082 &
[1] 2795 <-- PID OF SERVER
$ pstree -p
init(1)─┬─ ...
        :
        ├─gnome-terminal(2369)─┬─bash(2374)───mplayer(2459)───mplayer(2460)
        │                      ├─bash(2586)───pstree(2779)
        │                      ├─bash(2646)
        │                      ├─bash(2705)───server(2795) <-- YOUR SERVER
        │                      ├─gnome-pty-helpe(2373)
        │                      ├─{gnome-terminal}(2371)
        │                      ├─{gnome-terminal}(2372)
        │                      └─{gnome-terminal}(2375)
        :
$ telnet localhost 4082  <--- IN SAME TERMINAL
$ pstree -p
init(1)─┬─ ...
        :
        ├─gnome-terminal(2369)─┬─bash(2374)───mplayer(2459)───mplayer(2460)
        │                      ├─bash(2586)───pstree(2779)
        │                      ├─bash(2646)
        │                      ├─bash(2705)─┬─server(2795) <-- YOUR SERVER
        │                      │            └─telnet(2797) <-- TELNET SAME SHELL
        │                      ├─gnome-pty-helpe(2373)
        │                      ├─{gnome-terminal}(2371)
        │                      ├─{gnome-terminal}(2372)
        │                      └─{gnome-terminal}(2375)
        :

现在。服务器和shell 共享 相同的bash以及相应的最终用户输出 - stdout ...

您可以通过各种方式间谍处理流程的不同流。一种简单的方法是使用strace

$ sudo strace -ewrite -p 12345

其中12345是进程的PID。即;

$ sudo strace -e write=0,1,3,4 -p 2797
Process 2797 attached - interrupt to quit
select(4, [0 3], [], [3], NULL)         = 1 (in [0])
read(0, "frack!\n", 8129)               = 7
select(4, [0 3], [3], [3], {0, 0})      = 1 (out [3], left {0, 0})
send(3, "frack!\r\n", 8, 0)             = 8
 | 00000  66 72 61 63 6b 21 0d 0a                           frack!..          |