插座:过早断开

时间:2014-04-14 02:07:24

标签: c sockets network-programming posix

所以我在这里有一个简单的TCP echo服务器程序(IPv4),它在客户端发送FIN数据包之前从新客户端收到连接后似乎立即断开连接。 这是echo服务器代码:

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




#define MAX_BUFFER  1024
#define PORT        4000





int main()
{
  int lsock,csock, ret, in , i;
  int yes = 1;
  char buffer[MAX_BUFFER];
  char* c;
  struct sockaddr_in servaddr;
  struct sockaddr_in cliaddr; // connector's address information



  if((lsock = socket(AF_INET,SOCK_STREAM,0))<0){
      perror("socket");
      return -1;
  }

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



  if(bind(lsock,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1) {
            perror("bind");
            close(lsock);           
            return -1;
  }



  if(listen(lsock,5)==-1){
           perror("listen");
           close(lsock);
           return -1;
  }else{     
           printf("Server listening on port %i\n",PORT);
           system("gnome-terminal");

  }





  while(1){


     int len = sizeof(cliaddr);
     bzero(&cliaddr,len);



     if(csock = accept(lsock,(struct sockaddr*)&cliaddr,&len)==-1){
           perror("accept");
           exit(0);
     } 

     printf("New client connected....\n");

     in = recv(csock,(void*)&buffer,sizeof(buffer),0);

     if(in==-1){
            perror("recv");
            close(csock);
            exit(0);
     }else if(in==0){    
           printf("client disconnected\n");
           close(csock);

     }else{

       if(send(csock,(void*)buffer,sizeof(buffer),0)==-1){
        perror("send");
            close(csock);
       }

     }

  }



  return 0;
}

对于echo客户端应用程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <linux/ioctl.h>



#define MAX_BUFFER 1024

void die(char *s)
{
      perror(s);
      exit(1);
}


int main()

{

  int  connector,flags,r;
  int  port;
  int  set = 1;
  struct hostent*        host;
  struct in_addr         in;
  struct sockaddr_in     rmaddr;
  char   sendbuffer[MAX_BUFFER];
  char   recvbuffer[MAX_BUFFER];
  char   hostname[INET_ADDRSTRLEN];
  char*  exit = "quit";



  if((connector = socket(AF_INET,SOCK_STREAM,0))<0){
      perror("socket");
      return -1;
   }


  printf("\n");
  printf("Enter the remote hostname(URL/IP4 address): ");
  scanf("%s", hostname);
  printf("\n");



  printf("Enter the port number you wish to connect(on): ");
  scanf("%u", &port);
  printf("\n");

  if(port==0){
              printf("ERR0R: Port number must be between 1 & 65,535\n");
              printf("\n");
              printf("Enter the port number you wish to connect(on): ");
              scanf("%u", &port);
              printf("\n");        
  }

  host = gethostbyname(hostname);

  if(host==NULL){
         perror("hostname");
         return -1;
  }


  bzero(&rmaddr,sizeof(rmaddr));   
  rmaddr.sin_family = AF_INET;
  rmaddr.sin_port = htons(port);
  bcopy((char*)host->h_addr, (char*)&rmaddr.sin_addr.s_addr, host->h_length);  


  if(connect(connector,(struct sockaddr*)&rmaddr,sizeof(rmaddr))<0){
       perror("connect");
       close(connector);
       return -1;
  }else{
       printf("\n");
       printf("Connected to host: %s",hostname,"on port %u",port);
       printf("     type 'quit' to disconnect\n");
       printf("\n");
  }

  while(1){      
       int nbr,nbs; 
       nbr = 0;      

       printf(">");
       scanf("%s",sendbuffer);
       printf("\n");

       if(sendbuffer==exit){
       close(connector);
           return 0;
       }

       nbs = send(connector,(void*)&sendbuffer,strlen(sendbuffer),MSG_NOSIGNAL);

       printf("\n");
       printf("bytes sent: %i\n",nbs);
       printf("\n");

       if(nbs < 0){
        perror("send() failed");
            close(connector);
            return -1;
       }

       while(nbr < nbs){
     nbr = recv(connector,(void*)&recvbuffer,strlen(recvbuffer),MSG_NOSIGNAL);
         if(nbr < 0){
              perror("recv() failed");
                  close(connector);
          return -1;
         }else if(nbr==0){
          printf("recv(): connection closed prematurely");
                  close(connector);                     
          return -1;                
         }else if(nbr > 0){
              printf("bytes received: %i\n",nbr);
                  printf("\n");
          printf(">>");
                  printf("%s",recvbuffer);
                  printf("\n");

         }

       }

  }

  EXIT:
       close(connector);
       return 0;



}

现在当我编译代码然后执行它时,这是输出(在我使用echo客户端连接之后):

zermacr0yd@DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7/include $ ./ES
Server listening on port 4000
New client connected....
recvmsg: Socket operation on non-socket
zermacr0yd@DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7/include $

现在我已尝试将客户端连接到其他主机,例如端口80上的www.google.com,输出相同。这是:

zermacr0yd@DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $ ./EC

Enter the remote hostname(URL/IP4 address): www.google.com

Enter the port number you wish to connect(on): 80


Connected to host: www.google.com     type 'quit' to disconnect

>hi


bytes sent: 2

recv(): connection closed prematurelyzermacr0yd@DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $

因此很清楚连接正在通过(即服务器接收到SYN数据包),但之后会立即关闭连接。所以它似乎是recv()函数的一个问题,但它很可能是一个connect()问题。但是当我尝试连接到环回地址时服务器没有运行我得到:

zermacr0yd@DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $ ./EC

Enter the remote hostname(URL/IP4 address): 127.0.0.1

Enter the port number you wish to connect(on): 5000

connect: Connection refused
zermacr0yd@DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $

所以我很困惑:这是服务器端错误还是客户端错误?我认为服务器端的recv()函数失败,服务器关闭连接并关闭,然后客户端不知道服务器没有运行直到用户打开客户端尝试发送消息,不接收任何字节。很可能客户端断开连接,但看起来不太可能。

4 个答案:

答案 0 :(得分:3)

这条线错了。它每次都会评估为TRUE或FALSE(值0或1)。

if (csock = accept(lsock, (struct sockaddr*) & cliaddr, &len) == -1)
{
    perror("accept");
    exit(0);
}

因此,当您尝试对文件描述符0或1执行套接字操作时 - 通常是stdin和stdout - 您将看到错误:recvmsg: Socket operation on non-socket

将行更改为:

if ((csock = accept(lsock, (struct sockaddr*) & cliaddr, &len)) == -1)

答案 1 :(得分:2)

在您的原始服务器代码中,您有:

if(csock = accept(lsock,(struct sockaddr*)&cliaddr,&len)<0){
      perror("accept");
      exit(0);
}

您现在已更改为:

if(csock = accept(lsock,(struct sockaddr*)&cliaddr,&len)==-1){
    perror("accept");
    exit(0);
} 

但问题是一样的。这里,csock可能得到值0,它对应于标准输入,因此不是套接字。 (我假设为0,因为如果它变为1,您的服务器就会提前退出并显示accept的{​​{1}}错误消息。)这是因为{{1} }和perror()运算符的优先级高于<。您可以通过在作业周围添加一组括号来解决这个问题(我在Duck提供答案之前我已经明确说明了这一点),或者在作业之前放置作业。因为在你看到Duck的回答之前,你似乎并没有真正尝试我的任何建议,我将说明第二个建议:

==

您的客户端测试无效,因为您正在连接到HTTP端口,期待ECHO行为。除了Web服务器不接受=作为输入之外,您无法得出任何结论。

您的服务器代码设计不合理。它是单线程迭代服务器,但在迭代执行另一个阻塞csock = accept(lsock,(struct sockaddr*)&cliaddr,&len); if (csock == -1) { perror("accept"); exit(0); }  调用之前,它无法正确清理现有客户端连接。相反,客户端应该处理客户端连接,直到连接终止,然后循环回hi。有替代方法(有一个单独的线程处理客户端连接,或使用事件驱动的循环与accept())。但是,鉴于这似乎是一个学习项目,只需一次处理一个连接就可以了。但是,您需要一个内部循环来再次accept()之前完全处理客户端连接。在伪代码中:

select()/poll()/epoll()

上面的插图修复了服务器的另一个问题,因为你在新连接上编写完整的缓冲区,而你应该只写出读取的字节数,在你的情况下存储在变量中{ {1}}。所以,而不是:

accept()

请改为:

while (not done)
    new_conn = accept()
    while (new_conn != -1)
        result = read(new_conn, buf, bufsize)
        switch (result)
            case -1:
                perror("read") /* FALLTHROUGH */
            case 0:
                close(new_conn)
                new_conn = -1
                break
            default:
                write(new_conn, buf, result)
                break

答案 2 :(得分:1)

  1. 如Duck所述,调用accept()的行中存在运算符优先级问题。这解释了非套接字&#39;

  2. 上的套接字操作
  3. &#39;拒绝连接&#39;在无法找到服务器时发生在客户端。因此它与服务器代码无关。

  4. 在您的客户端中,您需要在检测到错误后立即调用perror(),而不是之后的三个printf()。否则你会损坏errno并打印错误的错误。

  5. 没有过早的断开连接&#39;这里。只是一个断开连接,以及你自己设计的一个非常误导性的错误信息。你正在循环阅读。有人必须断开一些时间。如果您将该客户端连接到真实的echo服务器,它将回显一个响应,然后它可能会关闭连接。

答案 3 :(得分:0)

因此,除了遵循Ducks建议并在服务器代码中添加接收循环之外,我意识到echo_client的部分问题是以下几行:

while(1){      
       int nbr,nbs; 
       nbr = 0;  

当我删除初始化nbr(接收的字节数)为零的行时,客户端不会断开连接,它实际上按照我的意图工作。