Socket Programming in C
Socket Programming in C
<=================================================>
CONTENTS
=======================================
1. Introduction
2. Different types of Internet Sockets
3. Structures
4. Conversions
5. IP Addresses
6. Important Functions
6.1. socket()
6.2. bind()
6.3. connect()
6.4. listen()
6.5. accept()
6.6. send()
6.7. recv()
6.8. sendto()
6.9. recvfrom()
6.10. close()
6.11. shutdown()
6.12. gethostname()
11. Copyright
1. INTRODUCTION
=======================================
Are you trying to learn c socket programming? Or do you think that it's hard
stuff?
Well, then you must read this basic tutorial to get basic ideas and concepts and
to start
to work with sockets. Don't expect to be a "socket programming master" after
reading this
tutorial. You'll only be that if you practice and read a lot.
In the first place I must explain what a socket is. In a very simple way, a socket
is a way
to talk to other computer. To be more precise, it's a way to talk to other
computers using
standard Unix file descriptors. In Unix, every I/O actions are done by writing or
reading
to a file descriptor. A file descriptor is just an integer associated with an open
file and it
can be a network connection, a terminal, or something else.
About the different types of internet sockets, there are many types but I'll just
describe two
of them - Stream Sockets (SOCK_STREAM) and Datagram Sockets (SOCK_DGRAM).
"And what are the differences between this two types?" you may ask. Here's the
answer:
Stream Sockets - they're error free; if you send through the stream socket
three
items "A,B,C", they will arrive in the same order - "A,B,C" ;
they use TCP ("Transmission Control Protocol") - this protocol
assures the items' order.
A lot more could be explained here about this two kind of sockets, but I think
this is enough
to get the basic concept of socket. Understanding what a socket is and this two
types of
internet sockets is a good start, but you need to learn how to "work" with them.
You'll learn
it in the next sections.
3. STRUCTURES
=======================================
The purpose of this section is not to teach you structures but to tell you how are
they
used in C socket programming. If you don't know what a structure is, my advice is
to read
a C Tutorial and learn it. For the moment, let's just say that a structure is a
data type
that is an aggregate, that is, it contains other data types, which are grouped
together into
a single user-defined type.
Structures are used in socket programming to hold information about the address.
struct sockaddr{
unsigned short sa_family; /* address family */
char sa_data[14]; /* 14 bytes of protocol address */
};
But, there's another structure (struct sockaddr_in) that help you to reference to
the socket's
elements.
struct sockaddr_in {
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port */
struct in_addr sin_addr; /* Internet Address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
Note: sin_zero is set to all zeros with memset() or bzero() (See examples bellow).
As you can see in both examples bellow (Stream Client and Server Client) , when I
declare for
example "client" to be of type sockaddr_in then I do client.sin_addr = (...)
struct in_addr {
unsigned long s_addr;
};
Finally, I think it's better talk about struct hostent. In the Stream Client
Example, you can
see that I use this structure. This structure is used to get remote host
information.
Here it is:
struct hostent
{
char *h_name; /* Official name of host. */
char **h_aliases; /* Alias list. */
int h_addrtype; /* Host address type. */
int h_length; /* Length of address. */
char **h_addr_list; /* List of addresses from name server. */
#define h_addr h_addr_list[0] /* Address, for backward compatibility. */
};
In the beginning, this structures will confuse you a lot, but after you start to
write some
lines, and after seeing the examples, it will be easier for you understanding
them. To see
how you can use them check the examples (section 8 and 9).
4. CONVERSIONS
=======================================
There are two types of byte ordering: most significant byte and least significant
byte.
This former is called "Network Byte Order" and some machines store their numbers
internally
in Network Byte Order.
There are two types you can convert: short and long.
Imagine you want to convert a long from Host Byte Order to Network Byte Order.
What would you
do? There's a function called htonl() that would convert it =) The following
functions are
used to convert :
You must be thinking why do you need this. Well, when you finish reading this
document, it will
all seems easier =) All you need is to read and a lot of practice =)
An important thing, is that sin_addr and sin_port (from struct sockaddr_in) must
be in Network
Byte Order (you'll see in the examples the functions described here to convert and
you'll start
to understand it).
5. IP ADRESSES
=======================================
In C, there are some functions that will help you manipulating IP addresses. We'll
talk about
inet_addr() and inet_ntoa() functions.
(...)
dest.sin_addr.s_addr = inet_addr("195.65.36.12");
(...)
(...)
char *IP;
ip=inet_ntoa(dest.sin_addr);
(...)
Remember that inet_addr() returns the address in Network Byte Order - so you don't
need to
call htonl().
6. IMPORTANT FUNCTIONS
=======================================
In this section I'll put the function' syntax, the header files you must include
to call it,
and little comments. Besides the functions mentioned in this document, there are
more, but
I decided to put only these ones here. Maybe I'll put them in a future version of
this
document =) To see examples of these functions, you can check the stream client
and stream
server source code (Sections 8 and 9)
6.1. socket()
=============
#include <sys/types.h>
#include <sys/socket.h>
socket() gives you a socket descriptor that you can use in later system
calls or
it gives you -1 on error (this is usefull for error checking routines).
6.2. bind()
===========
#include <sys/types.h>
#include <sys/socket.h>
bind() is used when you care about your local port (usually when you use
listen() )
and its function is to associate a socket with a port (on your machine). It
returns
-1 on error.
An important aspect about ports and bind() is that all ports bellow 1024 are
reserved.
You can set a port above 1024 and bellow 65535 (unless the ones being used
by other
programs).
6.3. connect()
==============
#include <sys/types.h>
#include <sys/socket.h>
6.4. listen()
=============
#include <sys/types.h>
#include <sys/socket.h>
listen() is used if you're waiting for incoming connections, this is, if you
want
someone to connect to your machine. Before calling listen(), you must call
bind()
or you'll be listening on a random port =) After calling listen() you must
call
accept() in order to accept incoming connection. Resuming, the sequence of
system calls
is:
1. socket()
2. bind()
3. listen()
4. accept() /* In the next section I'll explain how to use accept() */
6.5. accept()
=============
#include <sys/socket.h>
When someone is trying to connect to your computer, you must use accept() to
get the
connection. It's very simple to understand: you just get a connection if you
accept =)
Next, I'll give you a little example of accept() use because it's a little
different
from other functions.
(...)
sin_size=sizeof(struct sockaddr_in);
if ((fd2 = accept(fd,(struct sockaddr *)&client,&sin_size))==-1){ /* calls
accept() */
printf("accept() error\n");
exit(-1);
}
(...)
From now on, fd2 will be used for add send() and recv() calls.
6.6. send()
===========
This function is used to send data over stream sockets or CONNECTED datagram
sockets.
If you want to send data over UNCONNECTED datagram sockets you must use
sendto().
send() returns the number of bytes sent out and it will return -1 on error.
6.7. recv()
===========
int recv(int fd, void *buf, int len, unsigned int flags);
As I said above about send(), this function is used to send data over stream
sockets or
CONNECTED datagram sockets. If you want to send data over UNCONNECTED
datagram sockets
you must use recvfrom().
recv() returns the number of bytes read into the buffer and it'll return -1
on error.
6.8. sendto()
=============
int sendto(int fd,const void *msg, int len, unsigned int flags,
const struct sockaddr *to, int tolen);
As you can see, sendto() is just like send(). It has only two more arguments
: "to"
and "tolen" =)
sendto() is used for UNCONNECTED datagram sockets and it returns the number
of bytes
sent out and it will return -1 on error.
6.9. recvfrom()
===============
6.10. close()
=============
close(fd);
6.11. shutdown()
================
6.12. gethostname()
===================
#include <unistd.h>
This is important, because functions we saw above (like bind() and connect()) work
with IP
addresses.
To see you how you can get queima.ptlink.net IP address on c, I made a little
example:
#include <stdio.h>
#include <netdb.h> /* This is the header file needed for gethostbyname() */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
if (argc!=2){
printf("Usage: %s <hostname>\n",argv[0]);
exit(-1);
}
if ((he=gethostbyname(argv[1]))==NULL){
printf("gethostbyname() error\n");
exit(-1);
}
In this section, I'll show you a nice example of a stream server. The source code
is all
commented so that you ain't no possible doubts =)
Let's start:
main()
{
int sin_size;
server.sin_family = AF_INET;
server.sin_port = htons(PORT); /* Remember htons() from "Conversions" section?
=) */
server.sin_addr.s_addr = INADDR_ANY; /* INADDR_ANY puts your IP address
automatically */
bzero(&(server.sin_zero),8); /* zero the rest of the structure */
while(1){
sin_size=sizeof(struct sockaddr_in);
if ((fd2 = accept(fd,(struct sockaddr *)&client,&sin_size))==-1){ /* calls
accept() */
printf("accept() error\n");
exit(-1);
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> /* netbd.h is needed for struct hostent =) */
struct hostent *he; /* structure that will get information about remote
host */
struct sockaddr_in server; /* server's address information */
if (argc !=2) { /* this is used because our program will need one argument
(IP) */
printf("Usage: %s <IP Address>\n",argv[0]);
exit(-1);
}
server.sin_family = AF_INET;
server.sin_port = htons(PORT); /* htons() is needed again */
server.sin_addr = *((struct in_addr *)he->h_addr); /*he->h_addr passes "*he"'s
info to "h_addr" */
bzero(&(server.sin_zero),8);
buf[numbytes]='\0';
close(fd); /* close fd =) */
}
As I'm just a simple human, it's almost certain that there are some errors on this
document.
When I say errors I mean English errors (because my language is not the English)
but also
technical errors. Please email me if you detect any error =)
But you must understand that this is the first version of this document, so , it's
natural not
to be very complete (as matter of fact I think it is ) and it's also very natural
to have
stupid errors. However, I can be sure that source code presented in this document
works fine.
If you need help concerning this subject you can email me at <[email protected]>
SPECIAL THANKS TO: Ghost_Rider (my good old mate), Raven (for letting me write
this tutorial)
and all my friends =)
11. COPYRIGHT
=======================================
All copyrights are reserved. You can distribute this tutorial freely, as long you
don't change
any name or URL. You can't change a line or two, or add another lines, and then
claim that this
tutorial is yours. If you want to change something, please email me at
<[email protected]>.