PDA

View Full Version : Simple BSD Sockets Question




Soulstorm
Mar 1, 2008, 11:12 AM
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:
#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.



lee1210
Mar 1, 2008, 11:54 AM
Try this:

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.

yeroen
Mar 1, 2008, 12:15 PM
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.

Soulstorm
Mar 3, 2008, 03:21 PM
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:
#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:

[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:

what do the last lines mean (about ident responce)
why did the connection close by iself?
What am I supposed to read in order to get this done (if not already covered into the IRC specification)
some people tell me about telnet... What is this? I read the wikipedia article and I must admit I don't understand much...

yeroen
Mar 3, 2008, 03:42 PM
The definitive place to learn about all about the sockets API is "Unix Network Programming vol. 1", by Stevens:

http://www.amazon.com/Unix-Network-Programming-Addison-Wesley-Professional/dp/0131411551/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1204580529&sr=8-1

Cromulent
Mar 3, 2008, 04:19 PM
Here is some information on Ident.

http://en.wikipedia.org/wiki/Ident

http://tools.ietf.org/html/rfc1413

Looks like the IRC server can't find out who you are (as in your computer). Have you firewalled port 113?

Soulstorm
Mar 4, 2008, 10:06 AM
Here is some information on Ident.

http://en.wikipedia.org/wiki/Ident

http://tools.ietf.org/html/rfc1413

Looks like the IRC server can't find out who you are (as in your computer). Have you firewalled port 113?

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 (http://www.versiontracker.com/dyn/moreinfo/macosx/28564&vid=275508). But to no effect.

neoserver
Mar 4, 2008, 06:37 PM
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?

lee1210
Mar 4, 2008, 08:31 PM
what do the last lines mean (about ident responce)
why did the connection close by iself?
What am I supposed to read in order to get this done (if not already covered into the IRC specification)
some people tell me about telnet... What is this? I read the wikipedia article and I must admit I don't understand much...
[/LIST]

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

x704
Mar 5, 2008, 01:03 AM
I had the same problem... after you connect, you then need to send two thing to the server...


NICK MyWayCoolBotNick\r\n
USER ObjcBot 8 * :The Objective-C Bot\r\n


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:

PONG insertServerHere\r\n

Don't forget to join a channel!

JOIN #macdev\r\n

Soulstorm
Mar 5, 2008, 07:13 AM
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!

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.

Soulstorm
Mar 6, 2008, 05:29 AM
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!


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

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?

#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;
}

pilotError
Mar 6, 2008, 08:07 AM
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.

Soulstorm
Mar 6, 2008, 11:38 AM
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.

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:
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?

lee1210
Mar 6, 2008, 02:09 PM
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:
<clip>
Just this. How can I verify what I am sending using tcpdump?

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

Soulstorm
Mar 6, 2008, 04:41 PM
that worked, thanks a lot.

#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?