0

So here is the code for a TCP echo client. When I have the echo server listening on port 5000, the client connects but then once I type a message and hit enter, it disconnects automatically. What's the problem here?

source code(tcp_ec.c):

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



#define MAX_BUFFER 1024

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


int main()

{

  int    connector,flags,r;
  int  port;
  struct hostent*        host;
  struct in_addr         in;
  struct sockaddr_in     rmaddr;
  bool   connected = false;   
  char   sndbuffer[MAX_BUFFER];
  char   rcvbuffer[MAX_BUFFER];
  char   hostname[INET_ADDRSTRLEN];
  char*  exit = "quit";



  if((connector = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<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");
       return -1;
  }else{
       connected=true;
       printf("\n");
       printf("Connected to host: %s",hostname,"on port %u",port);
       printf("     type 'quit' to disconnect\n");
       printf("\n");
  }

  while(connected==true){

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

       if(sndbuffer==exit){
           close(connector);
           connected = false;
           return 0;
       }

       if(send(connector,(void*)sndbuffer,sizeof(sndbuffer),MSG_EOR||MSG_NOSIGNAL)<0){
            perror("send");
            close(connector);
            return -1;
       }

       if(recv(connector,(void*)rcvbuffer,sizeof(rcvbuffer),MSG_EOR||MSG_NOSIGNAL)<0){
            perror("recv");
            close(connector);
            return -1;
       }else{
        printf(">>");
            printf("%s\n",rcvbuffer);
            printf("\n");
       }




  }




}

The console output(program output file is "tc"):

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

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

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


Connected to host: 127.0.0.1     type 'quit' to disconnect

>asssr

sendmsg: Broken pipe
zermacr0yd@DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $ 

This is done when the echo_server is running on port 5000.

Phat_Albert
  • 119
  • 2
  • 13
  • 1) `if(sndbuffer==exit){}` you are comparing two stringpointers here. Use strncmp() or memcmp() here 2) `printf("%s\n",rcvbuffer);` recvbuffer will not be nulterminated by recv(). – wildplasser Jan 22 '14 at 20:48
  • wildplasser: I modified the code to the specs you listed but I STILL get the error message "sendmsg: Broken pipe" when I enter a message to be transmitted. – Phat_Albert Jan 22 '14 at 21:52
  • `echoclient.c:31:3: error: unknown type name ‘bool’` Are you using a C++ compiler ? – wildplasser Jan 22 '14 at 22:00
  • I am using Linux GCC 4.7.3 which is a native C compiler. – Phat_Albert Jan 22 '14 at 22:19
  • Standard C does not have bool types (but you can get them by `#include ` ). C++ does. Note: gcc switches to switch to C++ mode when your file name happens to be called *****.cpp or similar. – wildplasser Jan 22 '14 at 22:24
  • Your problem (and its solution) are described here: http://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly – Jeremy Friesner Jan 23 '14 at 03:14

3 Answers3

0

Replace:

if(send(connector,(void*)sndbuffer,sizeof(sndbuffer),MSG_EOR||MSG_NOSIGNAL)<0){
... and
if(recv(connector,(void*)rcvbuffer,sizeof(rcvbuffer),MSG_EOR||MSG_NOSIGNAL)<0){

By:

if(send(connector,(void*)sndbuffer,sizeof(sndbuffer),MSG_EOR|MSG_NOSIGNAL)<0){
... and
if(recv(connector,(void*)rcvbuffer,sizeof(rcvbuffer),MSG_EOR|MSG_NOSIGNAL)<0){

... plus some minor errors.

EDIT: complete corrected source

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

#define MAX_BUFFER 1024

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

int main() {
  int    connector,flags,r;
  int  port;
  struct hostent*        host;
  struct in_addr         in;
  struct sockaddr_in     rmaddr;
  bool   connected = false;
  char   sndbuffer[MAX_BUFFER];
  char   rcvbuffer[MAX_BUFFER];
  char   hostname[INET_ADDRSTRLEN];
  char*  exit = "quit";


  if((connector = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<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");
       return -1;
  }else{
       // connected=true;
       printf("\n");
       printf("Connected to host: %son port %u", hostname, port);
       printf("     type 'quit' to disconnect\n");
       printf("\n");
  }

  // while(connected==true){
  while(1){

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

       if(sndbuffer==exit){
           close(connector);
           // connected = false;
           return 0;
       }

       if(send(connector,(void*)sndbuffer,sizeof(sndbuffer),MSG_EOR|MSG_NOSIGNAL)<0){
            perror("send");
            close(connector);
            return -1;
       }

       if(recv(connector,(void*)rcvbuffer,sizeof(rcvbuffer),MSG_EOR|MSG_NOSIGNAL)<0){
            perror("recv");
            close(connector);
            return -1;
       }else{
        printf(">>");
            printf("%s\n",rcvbuffer);
            printf("\n");
       }
  }

return 0;
}

OUTPUT:

~/src/usenet$ ./a.out

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

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


Connected to host: 127.0.0.1on port 7     type 'quit' to disconnect

>aap

>>aap

>noot

>>noot

>mies

>>mies

>quit

>>quit

>^C

[the not reacting to "quit" is caused by the lack of strncmp() // memcmp(), plus "quit is actually "quit\n" (and not nul-terminated) ]

wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • Why? What's the difference? What effect does the change have? Can't see a single difference myself, but even if there is one, you have to explain. Nb the void* casts are redundant. – user207421 Jan 22 '14 at 23:26
  • The extra '|' (which I edited out, when adding the other two lines ...) And: yes the (void*) casts are redundant. – wildplasser Jan 22 '14 at 23:56
  • wildplasser, unfortunately EJP is correct: There is NO CHANGE in the program output response. *sigh* Something seems reeeeeeeeeallly wrong here. – Phat_Albert Jan 23 '14 at 01:05
  • The || operator is a logical-or, so its result will always be 0 or 1. The | operator is a bitwise-or, so its result will be the logical union of the bits of its two inputs. In this case, you definitely want the | operator, since || will not pass the correct value in to send()'s flags argument. – Jeremy Friesner Jan 23 '14 at 03:17
  • I added code to determine the number of bytes sent and sure enough, that number = -1. I wonder if the broken pipe error is the result of the socket closing the connection prematurely after successfully connecting. – Phat_Albert Jan 23 '14 at 15:04
  • Wildplasser what version of Linux are you using? – Phat_Albert Jan 23 '14 at 21:14
  • 3.2.0-58-generic #88-Ubuntu SMP Tue Dec 3 17:37:58 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux // gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 // I tested on localhost:8 – wildplasser Jan 23 '14 at 22:19
0

I thought the problem had to do with the Byte Ordering functions because I noticed that adding the header produces compiler warnings.But it turns out that what is causing the problem in the first place, along with the fact that calling the bind() function in the echo server application gives an EONOTSUPP error, is that the socket is not binding to a specific address! It is binding to all addresses and this GCC bug has been documented Here. So now the task at hand is to get the patch downloaded and installed and I'll see how it works from there.

Phat_Albert
  • 119
  • 2
  • 13
  • No, I tested it on localhost with the echo port (=8) and it just worked, after some modifications (such as the '||' issue) I think the OP just posted the wrong code, or has a different testbed. – wildplasser Jan 24 '14 at 00:05
  • I'm using Linux Mint 15 with kernel version 3.8.0-19 generic(along with the Linux headers). So yeah, I have a different testbed and some headers I had to extensively debug(like ).The newer kernel versions have some BIG problems, but I think it might be the headers with some of the byte ordering functions. – Phat_Albert Jan 24 '14 at 00:17
  • Din't blame the kernel or headers (others would have noticed) I think I'll add ny fixed-up source the the answer. – wildplasser Jan 24 '14 at 00:22
  • UPDATE: I got it to connect on localhost:8! The only trouble now is fixing the echo_server. But I don't see why it shouldn't work with the loopback address(127.0.0.1). I have a dual boot windows8/LinuxMint15 and using the loopback address works just find with winsock along with bind(). – Phat_Albert Jan 24 '14 at 00:25
  • Correction: localhost port=7 (the standard echo port) See my updated answer. – wildplasser Jan 25 '14 at 12:54
0

I've seen before that connect() to a non-existent port on 127.0.0.1 can return successfully, but it's decades ago and not on Linux. The technique I used then was to do a zero-length write, which would fail if the connect didn't really work. I never got to the bottom of it and I haven't seen the problem for over 20 years.

But you code has other problems.

You're ignoring the count returned by 'recv()' unless it is negative. This has two consequences:

  1. You may be printing junk afterwards.
  2. You aren't detecting end of stream from recv(), so you don't know when the peer has disconnected, so your loop continues and you send to a connection that has already been closed by the peer, which produces your 'broken pipe' problem. It is never correct to call recv() without storing the result into a variable.

You're also sending junk. Instead of 'sizeof sndbuffer', the count you supply to send() should be 'strlen(sndbuffer)' in this case, as you've just read a null-terminated string with 'fgets()'.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Well EJP, I tried making the changes you suggested but all to no avail. This really makes me think something is wrong with the sockets API because this problem with the connect() function also does the same thing with remote addresses. I used the Netcat program to test the loopback and wifi network interfaces and they both work fine. The connect() function is supposed to return successfully IF AND ONLY IF an ACK response from the server! So the fact that send() fails due to a broken pipe is expected because the socket is not connected. But if you know what can cause this error please tell me. – Phat_Albert Feb 06 '14 at 16:49