Intro / PlugI wrote this tutorial because I program network applications regularly, and I prefer to use BSD style socket calls whenever possible. While there was a great deal of information available on the internet for someone who wants to learn to program sockets, I found that not too much of it focused specifically on the application level. My goal is to provide myself and others with a single source starting point, for programming nearly and transport level or higher network applicationThis tutorial ( not quite yet finished ) was written by Matthew Fatheree matthewf@spyder-fonix.com . If you like it, love it, hate it, think it sucks, if you printed it out and then you used it as a bottom for your bird cage, or if you put salt on it and ate it... Please send questions, comments, and death threats to me. All joking aside feedback and corrections would be great. Thanks... ~Matt Fatheree http://www.spyder-fonix.com |
Getting Started |
||||||||||||||||||||||||||||
Extra Utilities Okay to start programming sockets it is assumed that you already
have a compiler, and a working IP stack you can utilize. Most of the code I've written here
was written for Linux machines but it should be fairly portable to windows IP stack, using SOCKET in place of int for winsocks as well. Header Files
Usefull Defines
Understanding TCP and UDPTCP, and UDP are the two main types of sockets people use when programming applications. TCP stands for transmission control protocol, while UDP stands for user datagram protocol. The main difference between TCP and UDP is TCP verifies that every packet sent gets recieved, and if data is missed, TCP will let you know, or retransmit it. UDP basically sends out a packet with an address and a port and hopes it gets there. Despite the lack of ACKs and NAKs UDP is just about as reliable as TCP.Understanding Clients and ServersWeather you are using TCP or UDP, every network connection is going to have two parts, and every network program is going to be one of these two parts. Occasionally you will have network programs that do both. The two types are Client and Server, also known as Active, and Passive, or Outgoing and Incomming.there is no defacto standard to be sure for describing the two parts... but Client Server is used allot. just for refrance. Client = Active = Outgoing Server = Passive = Incomming heres is a basic run down on the application level and network level, of the different socket connections. Client Communications
Server Communications
|
|
Blocking and non-blocking sockets
Working with sockets is similar to working with linux files using stdio. for sake of simplification lets say there are two working modes a TCP or UDP socket can be in. blocking or non blocking. in blocking mode the program will halt unit the desires socket action is preformed. take the following code for example.
/* sd is our socket file descriptor already open and listening attempting to accept a connection from a remote host */
if sd was a blocking socket, the program would halt there until a remote client tried to connect or an error was recieved, with a non-blocking socket the call would immeidiately return allowing your program to continue execution, and result would be set to the value of E_WOULDBLOCK. setting blocking or nonblocking mode
/* to set a socket into blocking mode use the following call */
|
Opening sockets |
||
Opening a socket is fairly trivial, below I have some code that will show you how to open a TCP, or UDP socket.
As you can see there is not much difference when opening a TCP socket or a UDP socket with the exception of passing the open call either SOCKSTREAM, for TCP, and SOCK_DGRAM for UDP. WHen this call returns you will either get a negative return value which means an error has occured, or you will get the 'socket handle' or the file descriptor used to control the socket. Allthough the socket is opened at this point, it does not have an associated TCP port, or IP address. |
Binding Sockets |
|||
For most people here is where things will start to get tricky. It is important to remember that for any given
socket connection there are four things that make that connection possible. A Local Ip address, a Local port,
a remote Ip address, and a Remote port. Before you are ready to connect, or accept on your socket to anything you must
first fill in a structure, that will tell the computer the correct values for these four parameters. Bind is the call
that ties a filled in structure to an IP address.
The code sample below will show you how to set up one of these structures, and bind it to a socket.
|
Connecting Sockets |
|
For most people binding, and connecting is where things will start to get tricky. It is important to remember that for any given
socket connection there are four things that make that connection possible. A Local Ip address, a Local port,
a remote Ip address, and a Remote port. Many times on a connecting ( active ) socket, we can allow the OS
to fill in our socket information for us. the code below shows how to make a connection to a remote host.
|
Accepting Sockets |
|
| If you are not opening a socket and making a connection there is a good chace
you are using the socket for accepting a connection. Acepting a connection really has two parts, on any socket
that will eventually accept a connection before you can call accept you must call listen(); listen takes two parameters, the socket you would like to listen on, and the Maximum number of sockets that you will accept on that socket at any given time. for example a call like this listen( socket, 5); would tell the OS that I can only allow five client sockets to connect at any one given time. the code example below illustrates how to accept a socket connection.
|
Sending Data |
|
| to send data over a socket the program will need to make a call to send();
if the socket is non-blocking the send call should return immediately with the number of bytes that were successfully
written to the sockets output queue. The return from send does not indicate the number of bytes received on the other end
of the connection nor does it indicate how large the tcp packet size is. But if you get a possetive return value on a send call
it means the OS has taken responsibility for transmitting that data. On a blocking socket send will return after all
of the data has been sent or on an error.
the following code example shows how you would send to a connected socket.
|
Receiving Data |
|
| to receive data over a socket the program will need to make a call to recv();
if the socket is non-blocking the recv call should return immediately with the number of bytes that were successfully
read from the sockets input queue. The return from recv does not indicate the number of bytes sent by the remote host,
nor does it indicate how large the tcp packet size was that was read. On a blocking socket recv will return after all
of the data specified has been received or on an error. It is also important to note that if recv() returns 0 ( zero ) that indicates
the remote socket has closed the connection.
the following code example shows how you would use recv on a connected socket.
|
Closing a socket |
|
| There are only two ways to close a socket and both are fairly trivial close( socket ); , and shutdown( socket, prio ); close( );, will close the socket imeediately, while shutdown, ill close the socket, after flushing certain buffers. you can specify what buffers shutdown should flush with an integer prio. The code example(s) below show how to use shutdown, and close, on a connected socket.
|
int init_tcp( int portno ) |
|
|
init_tcp( ), opens a tcp socket on the port specified by portno. init_tcp will return a negative on an error, or the open socket handle on success. if portno is equal to 0, init_tcp, will not bind the socket file descriptor to a port. the code for init_tcp( ), is below.
|
int init_udp( int portno ) |
|
|
init_udp( ), opens a udp socket on the port specified by portno. init_udp will return a negative on an error, or the open socket handle on success. if portno is equal to 0, init_udp, will not bind the socket file descriptor to a port. the code for init_udp( ), is below.
|
int tcp_connect( char *host, int portno ) |
|
|
tcp_connect will open a socket, and attempt to make a connection to the host and port specified.
tcp_connect will return a negative value if it fails, or the connected socket file descriptor on
success the code for tcp_connect( ), is below.
|
int tcp_connect_from( char *host, int portto, int portfrom ) |
|
|
tcp_connect_from will open a socket, and attempt to make a connection to the host and port specified,
in addition tcp_connect_from uses tcp_init to allow users to specify a local port to initiate the connection from.
tcp_connect_from will return a negative value if it fails, or the connected socket file descriptor on
success the code for tcp_connect_from( ), is below.
|
int sdprintf(int sock, const char *fmt, ...) |
|
|
sdprintf, is just like fprintf, but instead of taking a file pointer, it takes a files descriptor,
and uses send( ) to transmit the data, making it good for use with sockets. the code for sdprintf( ), is below.
|
int udp_send_file(char *fname, char *ipaddr, int portto, int portfrom) |
|
|
udp_send_file will attempt to open a file and send it across a UDP socket. it takes four parameters :
the first is a character pointer to a file name, the second is the IP address the file should be sent to, next is the port on the remote host that the file should be sent to, and finally, you can specify the port for the file to come from. If you specify port 0 for portfrom, a random port number will be picked. the code for udp_send_file( ), is below.
|
int tdp_send_file(char *fname, char *ipaddr, int portto, int portfrom) |
|
|
tcp_send_file will attempt to open a file and send it across a TCP socket. it takes four parameters :
the first is a character pointer to a file name, the second is the IP address the file should be sent to, next is the port on the remote host that the file should be sent to, and finally, you can specify the port for the file to come from. If you specify port 0 for portfrom, a random port number will be picked. the code for tcp_send_file( ), is below.
|
Thank you to:Tracy Thomas for providing some good feedback on some lesser known ( or lesser thought of aspects of the recv() call)...and to Philippe De Neve for pointing out some differences between windows and Linux ( *NIX ) socket programming and Chris Booth, who wrote... "you saved my ass in my new job thanks..." no problem Chris, I wish I knew how I saved your ass, but whatever.... |