==============================================================================
C-Scene Issue #2
BSD Sockets
Name: Stephen Pendleton
Nick: Ape
Email: pendleto@radix.net
==============================================================================
Part 1 - Connecting to a server
Lets face it, networking is cool. Nothing beats being able to connect
computers together and send data back and forth. But how do programs do that?
How can you communicate with other computers? One method is to use BSD Sockets.
BSD Sockets is an mechanism that lets computers communicate with each other.
Fortunately, sockets are really easy to program, and only have a few gotchas.
I am only going to cover the common case of stream sockets in the Internet
domain. There are other types of sockets: datagram, raw, sequenced packets,
and esoteric ones like reliably-delivered message sockets.
Forget those for now. When I use the term "sockets" in this article I mean
"stream sockets in the Internet domain".
Communicating with a remote computer using sockets is done using the following
steps:
1) Create a socket using socket().
2) Connect the socket to the remote computer using connect ().
3) Read and write data onto the socket using read() and send().
Step 1 is easy:
int csocket;
csocket = socket ( AF_INET,SOCK_STREAM,0 )
If socket() returns less than 0 then there was an error in creating the socket.
Step 2 is also easy but requires jumping through some hoops to resolve the IP
address. Here is all the code you need to connect to a remote computer given a
hostname or IP:
/* Assume servername is a string that contains the address */
/* e.g: irc.gate.net */
/* Assume port is a string that contains the port number */
/* eg: 6666 */
struct sockaddr_in server;
char port[8];
int csock;
int sockaddr_size;
struct hostent *hp;
csock=socket(AF_INET,SOCK_STREAM,0);
if (csock<0) {
perror("opening stream socket");
exit(1);
}
server.sin_family=AF_INET;
hp=gethostbyname(servername);
if (hp==0) {
fprintf(stderr,"%s: unknown host",servername);
exit(2);
}
memcpy((char *)&server.sin_addr,(char*)hp->h_addr,hp->h_length);
server.sin_port=htons(atoi(port));
printf("Connecting to %s (%s) on port %s...\n",servername
,inet_ntoa(server.sin_addr),port);
fflush(stdout);
if (connect(csock,(struct sockaddr *)&server, sizeof server)<0) {
perror("Connecting stream socket");
exit(1);
}
printf("Connected.\n");
At this point you are connected to the remote computer.
Step 3 is also easy, however there are a few points to remember. If you try to
read() on a normal socket and there is no data being sent from the remote
computer it will "block". In otherwords, the program will hang at that point
until data gets sent to you. This is in general a bad idea. Fortunately, there
is a way around this. Using select() you can check to see if there is data
waiting to be read on the socket. Here is some code to read data off a socket:
void handle_socket(int csock)
{
struct timeval to;
fd_set rread;
int socketopen=1;
int sr,rval;
char * buf;
// allocate memory for the buffer that holds the incoming socket data
buf=(char *)malloc(3000*sizeof(char));
if (buf==NULL){return;}
do {
FD_ZERO(&rread); // clear the fd_set
FD_SET(csock,&rread); // indicate that we want to
// check the csock socket only
memset((char *)&to,0,sizeof(to)); // clear the timeval struct
to.tv_sec=5; // timeout select after 5 secs
// do the select. select returns > 0 if there is a read event on the
socket // e.g. data waiting to be read
sr=select(csock+1, &rread, (fd_set *)0, (fd_set *)0, &to);
if (sr < 0) {
perror("select");
continue;
}
// there was an event on the socket
if (sr> 0)
{
// yeah. data is waiting to be read
if (FD_ISSET(csock,&rread))
{
// read the up to 2999 bytes from inbound socket
if ((rval=read(csock,buf,2999)) <= 0)
{
printf ("ending connection\n"); // the other end of the
// socket has closed
socketopen=0;
}
else
buf[rval]=0; // terminate the string
// in the buffer
}
else
do_some_other_stuff_here(); // No data was waiting to
// be read. Do some other
// thing here.
} while (socketopen); // loop until remote
// socket closes.
}
Select lets you check to see if there is an event occuring on the socket..
Events can can be:
1) Data waiting to be read.
2) Socket is ready to write to.
3) There is an exceptional condition. This occurs when out of band data is
pending. This is out of scope for this article.
For case 1 you want to do:
sr=select(csock+1, &rread, (fd_set *)0, (fd_set *)0, &to);
For case 2 you want:
sr=select(csock+1, (fd_set *)0, &wwrite, (fd_set *)0, &to);
You can also check for muliple cases by doing:
sr=select(csock+1, &rread, &wwrite, (fd_set *)0, &to);
Basically, passing a fd_set structure as the 2nd argument tells select that
you are looking for read events, passing a fd_set structure as the 3rd argument
tells it you are looking for write events.
Before you pass these fd_set structures you have to make sure that they are
initialized properly by doing:
FD_ZERO(&rread); // clear the fd_set
FD_SET(csock,&rread);
The FD_SET indicates that we only want to check the csock socket. It is
possible to check multiple sockets at the same time, but that is left as an
excercise for the reader.
Writing to a socket is very easy. You can use select() with case 2 to make
sure the socket is ready to be written and then do:
send(csock,buf,len,0);
Where buf contains the data you want to send and len is the number of bytes in
buf. The last parameter is used for esoteric purposes is are out of the scope
of the article.
You can close your socket elegantly by doing the following:
shutdown(csock,2);
close(csock);
Thats all there is to connect to a server and send and receive data. Next
issue I will talk about how to create your own server program. Its a little
more complex, but not much.
C Scene Official Web Site :
http://cscene.oftheinter.net
C Scene Official Email :
cscene@mindless.com
This page is Copyright © 1997 By