Simple BSD Sockets Question

Discussion in 'Mac Programming' started by Soulstorm, Mar 1, 2008.

  1. macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #1
    As you may remember from an older thread, I am diving into network connections, using the BSD sockets. My ultimate goal is to make a very basic IRC client.

    However, I am stuck.

    I have created a commend line tool in C and I have the following code:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    
    #define PORT 6667 // the port client will be connecting to 
    
    #define MAXDATASIZE 1000 // max number of bytes we can get at once 
    
    int main(int argc, char *argv[])
    {
        int sockfd, numbytes;  
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr; // connector's address information 
    	
    	he = gethostbyname("efnet.teleglobe.net");
    	sockfd = socket(PF_INET, SOCK_STREAM, 0);
    	if (sockfd < 0) {
    		printf("error creating the socket\n");
    	}
        their_addr.sin_family	= AF_INET;
    	their_addr.sin_port		= htons(PORT);
    	their_addr.sin_addr		= *((struct in_addr *)he->h_addr);
    	memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
    
    	
    	if (connect(sockfd, (struct sockaddr *)&their_addr,
    				sizeof their_addr) == -1) {
            perror("connect\n");
            exit(1);
        }
    	else
    		printf("You got the connection to the server!\n");
    	char p[] = "join #macfilez";
    	send(sockfd, p, sizeof(p), 0);
    	
    	recv(sockfd, buf, sizeof(buf), 0);
    	buf[numbytes] = '\0';
    	printf("Received: %s",buf);
    	close(sockfd);
    	
        return 0;
    } 
    The server I put was a server running on IRC's EFNet network. I was supposed to receive a message, from the server, right? Not only this doesn't happen, my program also crashes without showing anything! (the debugger comes up with no explanation).

    Any ideas why? Actually, I must have made a newbie mistake due to my ignorance. Please enlighten me.
     
  2. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    Try this:
    Code:
    include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    
    #define PORT 6667 // the port client will be connecting to
    
    #define MAXDATASIZE 1000 // max number of bytes we can get at once
    
    int main(int argc, char *argv[])
    {
        int sockfd, numbytes;
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr; // connector's address information
    
        he = gethostbyname("efnet.teleglobe.net");
        sockfd = socket(PF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
          printf("error creating the socket\n");
        }
        their_addr.sin_family       = AF_INET;
        their_addr.sin_port         = htons(PORT);
        their_addr.sin_addr         = *((struct in_addr *)he->h_addr);
        memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
    
    
        if (connect(sockfd, (struct sockaddr *)&their_addr,
                            sizeof their_addr) == -1) {
          perror("connect\n");
          exit(1);
        } else {
          printf("You got the connection to the server!\n");
          char p[] = "join #macfilez";
          send(sockfd, p, size(p), 0);
          fprintf(stderr,"After send, entering recv loop\n");
          numbytes=0;
          while(numbytes == 0) {
            fprintf(stderr,"In recv loop\n");
            numbytes=recv(sockfd, buf, sizeof(buf)-1, 0); //-1 b/c you will null terminate
          }
          fprintf(stderr,"After recv loop, %d bytes read.\n",numbytes);
          buf[numbytes] = '\0';
          fprintf(stderr,"After null term, %d bytes read.\n",numbytes);
          printf("Received: %s",buf);
          fflush(stdout);
          close(sockfd);
       }
    
        return 0;
    }
    
    It's not "done", but a message is read from the server:
    NOTICE AUTH :*** Processing connection to efnet.teleglobe.net

    I'm not familiar with irc, but hopefully this will point you in the right direction.

    Also:
    http://www.macosxhints.com/article.php?story=2003110513201777

    That might help for doing the identd the server is expecting.
     
  3. macrumors 6502a

    yeroen

    Joined:
    Mar 8, 2007
    Location:
    Cambridge, MA
    #3
    It's crashing because numbytes is never intialized or indeed set anywhere, so the expression buf[numbytes] = '\0'; on line 44 is an invalid memory access.

    You can adapt the above poster's changes to put you back on path.
     
  4. thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #4
    I managed to proceed a little further with your help, guys. I now have a little program that receives everything that a server says after trying to connect.

    That's the source:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    
    #define PORT 6667 // the port client will be connecting to 
    
    #define MAXDATASIZE 2000 // max number of bytes we can get at once 
    
    int main(int argc, char *argv[])
    {
        int sockfd, numbytes;
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr; // connector's address information
    	
        he = gethostbyname("efnet.teleglobe.net");
        sockfd = socket(PF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
    		printf("error creating the socket\n");
        }
        their_addr.sin_family       = AF_INET;
        their_addr.sin_port         = htons(PORT);
        their_addr.sin_addr         = *((struct in_addr *)he->h_addr);
        memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
    	
    	
        if (connect(sockfd, (struct sockaddr *)&their_addr,
    				sizeof their_addr) == -1) {
    		perror("connect\n");
    		exit(1);
        } 
    	else {
    		printf("You got the connection to the server!\n");
    		
    		fprintf(stderr,"After send, entering recv loop\n");
    		numbytes = 1;
    		while(numbytes != 0) {
    			numbytes = 0;
    			memset(buf, 0, sizeof(buf));
    			//fprintf(stderr,"In recv loop\n");
    			numbytes=recv(sockfd, buf, sizeof(buf)-1, 0); //-1 b/c you will null terminate
    			buf[numbytes] = '\0';
    			printf("Received: %s",buf);
    		}
    		
    		char command[] = "ping";
    		
    		send(sockfd, command, sizeof(command), 0);
    		
    		numbytes = 1;
    		while(numbytes != 0) {
    			numbytes = 0;
    			memset(buf, 0, sizeof(buf));
    			//fprintf(stderr,"In recv loop\n");
    			numbytes=recv(sockfd, buf, sizeof(buf)-1, 0); //-1 b/c you will null terminate
    			buf[numbytes] = '\0';
    			printf("Received: %s",buf);
    		}
    		
    		//fprintf(stderr,"After recv loop, %d bytes read.\n",numbytes);
    		//fprintf(stderr,"After null term, %d bytes read.\n",numbytes);
    		
    		fflush(stdout);
    		close(sockfd);
    	}
    	return 0;
    }
    However, this is my result:

    Code:
    [Session started at 2008-03-03 23:17:59 +0200.]
    You got the connection to the server!
    After send, entering recv loop
    Received: NOTICE AUTH :*** Processing connection to efnet.teleglobe.net
    Received: NOTICE AUTH :*** Looking up your hostname...
    NOTICE AUTH :*** Checking Ident
    NOTICE AUTH :*** Found your hostname
    Received: NOTICE AUTH :*** No Ident response
    Received: ERROR :Closing Link: 127.0.0.1 (Connection timed out)
    Received: Received: 
    The last lines are shown after I leave the program running for a while... so I have some questions:
    1. what do the last lines mean (about ident responce)
    2. why did the connection close by iself?
    3. What am I supposed to read in order to get this done (if not already covered into the IRC specification)
    4. some people tell me about telnet... What is this? I read the wikipedia article and I must admit I don't understand much...
     
  5. macrumors 6502a

    yeroen

    Joined:
    Mar 8, 2007
    Location:
    Cambridge, MA
    #5
  6. macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #6
  7. thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #7
    I am using airport express to connect to the internet, which means I am behind a router. But I have forwarded this port to no effect.

    EDIT: I still have the same reason with identd. I thought I needed an identd server, and I downloaded this one. But to no effect.
     
  8. macrumors 6502

    Joined:
    Apr 24, 2003
    #8
    I don't know much about the IRC protocol, but perhaps the server is closing the connection since you aren't sending anything to it? Don't most IRC servers have a PNG/QNG (ping/pong) challenge system to see if you're still there?
     
  9. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #9
    The last lines mean the server is rejecting the connection because it can't identify you (the identd issue).

    The connection closed because the server terminated it when it was unable to identify you.

    Other posters have pointed to sources of ident information, this is what I would look at next. This leads into your last question, you can try:
    telnet localhost 113
    from the terminal and see if you get a reply. if you do, and you do have your router forwarding TCP port 113 to the IP of your machine, the remote server should be OK with getting your ident that way.

    The other use for telnet in this case is to evaluate what the server will send, test your responses, etc.

    You can run:
    telnet efnet.teleglobe.net 6667
    And this will open a connection to the server you are trying to connect to with your program. From here you can enter commands and see what the server's response is. this gives you an easy means of validating what you need to listen for in your program and what the appropriate replies are without constantly modifying your program.

    Take a look at ident and see if you can get that going first. That seems to be the primary stumbling block right now.

    -Lee
     
  10. macrumors regular

    x704

    Joined:
    Apr 15, 2006
    #10
    I had the same problem... after you connect, you then need to send two thing to the server...

    Or something like that... as I recall thats where the server stopped caring about my connection and terminated me. the next thing you would need to do is reply to ping messages, otherwise the server will get mad at you:

    Don't forget to join a channel!

     
  11. thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #11
    Thanks a lot! That seemed to have fixed the problem! I connected using the terminal! Now all I have to do is the rest (build the program).

    lee1210, thanks a lot. i ran the command you gave me into the terminal, and then it worked. However, the ident could not be resolved.
     
  12. thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #12

    OK, I managed to do this using the terminal, but there is still something I miss.

    With the command "telnet efnet.teleglobe.net 6667" I get to open a telnet communication with the server. The same thing happens when I use the connect() function in C++. But afterwards, how can I send those 2 commands I need (NICK and USER) using C++? In the terminal, typing

    Code:
    NICK MyWayCoolBotNick\r\n
    USER ObjcBot 8 * :The Objective-C Bot\r\n
    is sufficient. But in C++, I try to use the send() command but with no effect. What am I doing wrong?

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    
    #define PORT 6667 // the port client will be connecting to 
    
    #define MAXDATASIZE 2000 // max number of bytes we can get at once 
    
    int main(int argc, char *argv[])
    {
    	int sockfd, numbytes;
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr; // connector's address information
    	
        he = gethostbyname("efnet.teleglobe.net");
        sockfd = socket(PF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
    		printf("error creating the socket\n");
        }
        their_addr.sin_family       = AF_INET;
        their_addr.sin_port         = htons(PORT);
        their_addr.sin_addr         = *((struct in_addr *)he->h_addr);
        memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
    	
    	
        if (connect(sockfd, (struct sockaddr *)&their_addr,
    				sizeof their_addr) == -1) {
    		perror("connect\n");
    		exit(1);
        } 
    	else {
    		printf("You got the connection to the server!\n");
    		
    		fprintf(stderr,"After send, entering recv loop\n");
    		numbytes = 0;
    		do {
    			numbytes = 0;
    			memset(buf, 0, sizeof(buf));
    			//fprintf(stderr,"In recv loop\n");
    			numbytes=recv(sockfd, buf, sizeof(buf)-1, 0); //-1 b/c you will null terminate
    			buf[numbytes] = '\0';
    			printf("Received: %s",buf);
    		} while (numbytes != 0);
    		
    		//char command[] = "PING\r\n";
    		char nickCommand[] = "NICK MyWayCoolBotNick\r\n";
    		char userCommand[] = "USER ObjcBot 8 * :The Objective-C Bot\r\n";
    		//system("telnet efnet.teleglobe.net 6667");
    		
    		//send(sockfd, command, sizeof(command), 0);
    		send(sockfd, nickCommand, sizeof(nickCommand), 0);
    		send(sockfd, userCommand, sizeof(userCommand), 0);
    		
    		printf("exited first loop!!!\n");
    		do {
    			
    			numbytes = 0;
    			memset(buf, 0, sizeof(buf));
    			//fprintf(stderr,"In recv loop\n");
    			numbytes=recv(sockfd, buf, sizeof(buf)-1, 0); //-1 b/c you will null terminate
    			buf[numbytes] = '\0';
    			printf("Received: %s",buf);
    		} while (numbytes !=0);
    		
    		
    		
    		fflush(stdout);
    		close(sockfd);
    	}
    	return 0;
    }
     
  13. macrumors 68020

    pilotError

    Joined:
    Apr 12, 2006
    Location:
    Long Island
    #13
    Do you get anything back on the line or are you just getting terminated?

    You can also try doing a tcpdump to see if your actually sending the data and see if there's something that your not picking up.

    I doubt this is an issue, but you could also trying turning on tcp nodelay on the socket level.
     
  14. thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #14
    I don't know how to use tcpdump and I don't understand the man pages.

    And I am just getting terminated. This is my output log:
    Code:
    christos-sotirious-imac:~ soulstorm$ /Users/soulstorm/CoreFoundation\ tool/build/Release/CoreFoundation\ tool ; exit;
    You got the connection to the server!
    After send, entering recv loop
    Received: NOTICE AUTH :*** Processing connection to efnet.teleglobe.net
    Received: NOTICE AUTH :*** Looking up your hostname...
    NOTICE AUTH :*** Checking Ident
    NOTICE AUTH :*** Found your hostname
    Received: NOTICE AUTH :*** No Ident response
    ********************time passes ******************
    Received: ERROR :Closing Link: 127.0.0.1 (Connection timed out)
    Received: exited first loop!!!
    Received: logout
    [Process completed]
    
    Just this. How can I verify what I am sending using tcpdump?
     
  15. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #15
    I would move the two send statements with the nick and user BEFORE the first input loop.

    I realize that I lead you astray on this point with the reading until numbytes is 0, because I wasn't totally sure what the protocol was like. Essentially you'll stay in that loop until the server disconnects you and there is nothing left to read (I believe).

    If you send nick and user first, what you get back from the server should be a welcome message, etc. Afterwards you should be able to send your join command.

    This is where experimenting with telnet should come in handy, because you can see what to send to the server and when, and what its responses will be.

    I can't comment too much on tcpdump, but if you were to use it you would likely only want to listen to connections to the efnet server on the port you are using, so you only see the tcp activity from your program. If you don't have a filter like this you will probably have a very hard time viewing the output because it will be full of other tcp communications to and from your computer.

    -Lee
     
  16. thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #16
    that worked, thanks a lot.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    
    #define PORT 6667 // the port client will be connecting to 
    
    #define MAXDATASIZE 2000 // max number of bytes we can get at once 
    char nickCommand[] = "NICK MyWayCoolBotNick\r\n";
    char userCommand[] = "USER ObjcBot 8 * :The Objective-C Bot\r\n";
    
    char allCommands[] = "NICK MyWayCoolBotNick\r\nUSER ObjcBot 8 * :The Objective-C Bot\r\n";
    
    int main(int argc, char *argv[])
    {
    	int sockfd, numbytes;
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr; // connector's address information
    	
        he = gethostbyname("efnet.teleglobe.net");
        sockfd = socket(PF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
    		printf("error creating the socket\n");
        }
        their_addr.sin_family       = AF_INET;
        their_addr.sin_port         = htons(PORT);
        their_addr.sin_addr         = *((struct in_addr *)he->h_addr);
        memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
    	
    	
        if (connect(sockfd, (struct sockaddr *)&their_addr,
    				sizeof their_addr) == -1) {
    		perror("connect\n");
    		exit(1);
        }
    	else {
    		//send(sockfd, nickCommand, sizeof(nickCommand), 0);
    		//send(sockfd, userCommand, sizeof(userCommand), 0);
    		send(sockfd, allCommands, sizeof(allCommands), 0);
    		printf("You got the connection to the server!\n");
    		
    		fprintf(stderr,"After send, entering recv loop\n");
    		numbytes = 0;
    		
    		
    		do {
    			numbytes = 0;
    			memset(buf, 0, sizeof(buf));
    			//fprintf(stderr,"In recv loop\n");
    			numbytes=recv(sockfd, buf, sizeof(buf)-1, 0); //-1 b/c you will null terminate
    			buf[numbytes] = '\0';
    			printf("Received: %s",buf);
    		} while (numbytes != 0);
    		
    		//char command[] = "PING\r\n";
    		
    		//system("telnet efnet.teleglobe.net 6667");
    		
    		//send(sockfd, command, sizeof(command), 0);
    		
    		printf("exited first loop!!!\n");
    		do {
    			
    			numbytes = 0;
    			memset(buf, 0, sizeof(buf));
    			//fprintf(stderr,"In recv loop\n");
    			numbytes=recv(sockfd, buf, sizeof(buf)-1, 0); //-1 b/c you will null terminate
    			buf[numbytes] = '\0';
    			printf("Received: %s",buf);
    		} while (numbytes !=0);
    		
    		
    		
    		fflush(stdout);
    		close(sockfd);
    	}
    	return 0;
    }
    I noticed something strange, though. If I send the commands about the NICK and USER, I will get nothing. Else, I get the correct result (the correct response from the server). Why does this happen? I mean, is it not the same?
     

Share This Page