LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Linux Answers > Programming
User Name
Password

Notices


By nhydra at 2006-11-11 08:44
BSD Sockets programming in C with examples - HOWTO
Author: Svetoslav P. Chukov <mhydra@users.sourceforge.net>, <hydra@nhydra.org>

----------------------------------------------------------------------------
Table of contents

1. Introduction

2. Used parts of BSD Sockets API.
2.1 Functions list.
2.2 Structures and data types.

3. Programming steps in a simple examples.

3.1 Create the socket.
3.1.1 Network socket.
3.1.2 Local socket.

3.2 Initialize the socket structure and make a socket adress.

3.3 Side specific options.
3.3.1 Client side.
3.3.1.1 Connect to the server.

3.3.2 Server side
3.3.2.1 Binding the socket.
3.3.2.2 Listening for incoming connections.
3.3.3.3 Accepting connections.

3.4 Transferring data.
3.4.1 Sending
3.4.2 Receiving

3.5 Advanced tricks.

4. Full example source code.
4.1 network.h
4.2 network.c
4.3 server.c
4.4 client.c

5. How to compile it?

6. Description.

7. Copyright.


----------------------------------------------------------------------------

BSD Sockets programming in C - HOWTO

1. Introduction
------------------------------------------------------------------------------

2. Used parts of BSD Sockets API.
------------------------------------------------------------------------------

2.1 Functions list.
------------------------------------------------------------------------------
Functions:

int accept (int socket, struct sockaddr *addr, socklen_t *length_ptr)
---------------------------------------------------------------------
This function is used to accept a connection request on the server socket socket.
The accept function waits if there are no connections pending, unless the
socket socket has nonblocking mode set. (You can use select to wait for
a pending connection, with a nonblocking socket.) See File Status Flags,
for information about nonblocking mode.

The addr and length-ptr arguments are used to return information about
the name of the client socket that initiated the connection.
See Socket Addresses, for information about the format of the information.

Accepting a connection does not make socket part of the connection.
Instead, it creates a new socket which becomes connected. The normal
return value of accept is the file descriptor for the new socket.

After accept, the original socket socket remains open and unconnected,
and continues listening until you close it. You can accept further
connections with socket by calling accept again.

If an error occurs, accept returns -1.

int bind (int socket, struct sockaddr *addr, socklen_t length)
---------------------------------------------------------------------
The bind function assigns an address to the socket socket.
The addr and length arguments specify the address; the detailed format of
the address depends on the namespace. The first part of the address is always
the format designator, which specifies a namespace, and says that the address
is in the format of that namespace.

The return value is 0 on success and -1 on failure.

int connect (int socket, struct sockaddr *addr, socklen_t length)
---------------------------------------------------------------------
The connect function initiates a connection from the socket with file
descriptor socket to the socket whose address is specified by the addr and
length arguments. (This socket is typically on another machine, and it must
be already set up as a server.) See Socket Addresses, for information about
how these arguments are interpreted.

Normally, connect waits until the server responds to the request before it
returns. You can set nonblocking mode on the socket socket to make connect
return immediately without waiting for the response. See File Status Flags,
for information about nonblocking mode.

The normal return value from connect is 0. If an error occurs, connect returns -1.

uint16_t htons (uint16_t hostshort)
---------------------------------------------------------------------
This function converts the uint16_t integer hostshort from host byte order
to network byte order.

uint32_t htonl (uint32_t hostlong)
---------------------------------------------------------------------
This function converts the uint32_t integer hostlong from host byte order to network byte order.

This is used for IPv4 Internet addresses.

int listen (int socket, unsigned int n)
---------------------------------------------------------------------
The listen function enables the socket socket to accept connections,
thus making it a server socket.

The argument n specifies the length of the queue for pending connections.
When the queue fills, new clients attempting to connect fail with ECONNREFUSED
until the server calls accept to accept a connection from the queue.

The listen function returns 0 on success and -1 on failure.

int read (int socket, void *buffer, size_t size)
---------------------------------------------------------------------
If nonblocking mode is set for socket, and no data are available to be read,
read fails immediately rather than waiting. See File Status Flags, for information
about nonblocking mode.

This function returns the number of bytes received, or -1 on failure.

int send (int socket, void *buffer, size_t size, int flags)
---------------------------------------------------------------------
The send function is like write, but with the additional flags flags.
The possible values of flags are described in Socket Data Options.

This function returns the number of bytes transmitted, or -1 on failure.
If the socket is nonblocking, then send (like write) can return after sending
just part of the data. See File Status Flags, for information about nonblocking mode.

Note, however, that a successful return value merely indicates that the message
has been sent without error, not necessarily that it has been received without error.

int shutdown (int socket, int how)
---------------------------------------------------------------------
The shutdown function shuts down the connection of socket socket.
The argument how specifies what action to perform:

0 - Stop receiving data for this socket. If further data arrives, reject it.

1 - Stop trying to transmit data from this socket. Discard any data waiting
to be sent. Stop looking for acknowledgement of
data already sent; don't retransmit it if it is lost.

2 - Stop both reception and transmission.

The return value is 0 on success and -1 on failure.

int socket (int namespace, int style, int protocol)
---------------------------------------------------------------------
This function creates a socket and specifies communication style style,
which should be one of the socket styles listed in Communication Styles.
The namespace argument specifies the namespace; it must be PF_LOCAL
(see Local Namespace) or PF_INET (see Internet Namespace). protocol designates
the specific protocol (see Socket Concepts); zero is usually right for protocol.

The return value from socket is the file descriptor for the new socket, or -1
in case of error.

The file descriptor returned by the socket function supports both read and
write operations. However, like pipes, sockets do not support file positioning operations.

2.2 Structures and data types.
------------------------------------------------------------------------------

struct sockaddr
---------------------------------------------------------------------

The struct sockaddr type itself has the following members:

hort int sa_family - This is the code for the address format of this address.
It identifies the format of the data which follows.

char sa_data[14] - This is the actual socket address data, which is
format-dependent. Its length also depends on the format,
and may well be more than 14. The length 14 of sa_data is
essentially arbitrary.

AF_LOCAL - This designates the address format that goes with the
local namespace. (PF_LOCAL is the name of that namespace.)
See Local Namespace Details, for information about this
address format.

AF_UNIX - This is a synonym for AF_LOCAL. Although AF_LOCAL is
mandated by POSIX.1g, AF_UNIX is portable to more systems.
AF_UNIX was the traditional name stemming from BSD,
so even most POSIX systems support it. It is also the
name of choice in the Unix98 specification. (The same
is true for PF_UNIX vs. PF_LOCAL).

AF_FILE - This is another synonym for AF_LOCAL, for compatibility.
(PF_FILE is likewise a synonym for PF_LOCAL.)

AF_INET - This designates the address format that goes with the
Internet namespace. (PF_INET is the name of that namespace.)
See Internet Address Formats.

AF_INET6 - This is similar to AF_INET, but refers to the IPv6 protocol.
(PF_INET6 is the name of the corresponding namespace.)

AF_UNSPEC - This designates no particular address format. It is used
only in rare cases, such as to clear out the default
destination address of a “connected” datagram socket.

The corresponding namespace designator symbol PF_UNSPEC exists for completeness,
but there is no reason to use it in a program.

struct sockaddr_in
---------------------------------------------------------------------
This is the data type used to represent socket addresses in the Internet namespace.
It has the following members:

sa_family_t sin_family - This identifies the address family or format of the
socket address. You should store the value AF_INET
in this member. See Socket Addresses.

struct in_addr sin_addr - This is the Internet address of the host machine.
See Host Addresses, and Host Names, for how to get
a value to store here.

unsigned short int sin_port - This is the port number.

When you call bind or getsockname, you should specify sizeof (struct sockaddr_in)
as the length parameter if you are using an IPv4 Internet namespace socket address.

3. Programming steps in a simple examples.
------------------------------------------------------------------------------

3.1 Create the socket.
Before to use any socket you have to create it. This can be done via
socket () function. There is two general types of sockets. A network socket and
local socket. TAKE A LOOK ABOUT THE FOLLOWING LINES HOW TO CREATE THE SOCKET.

3.1.1 Network socket.
------------------------------------------------------------------------------
Network socket is used about connecting to a network. Here you are some
example how to do that:

int sock;
sock = socket ( PF_INET, SOCK_STREAM, IPPROTO_TCP );

The "PF_INET" argument specifies that the socket will be internet socket.
Let's take a look about any of the arguments.

PF_INET - specifies the socket type (in our case - internet socket)
SOCK_STREAM - specifies that the connection will be via stream.
IPPROTO_TCP - the used protocol will be TCP.

In the above example we make a internet socket with TCP packets, simple and easy ...

3.1.1 Local socket.
------------------------------------------------------------------------------
Local socket is used about local connecting. It is used in Interprocess communication
Here you are some example how to do that:

int sock;
sock = socket ( PF_LOCAL, SOCK_DGRAM, 0 );

The "PF_INET" argument specifies that the socket will be internet socket.
Let's take a look about any of the arguments.

PF_LOCAL - specifies the socket type (in our case - local socket)
SOCK_DGRAM - specifies that the connection will be via diagrams.
0 - no protocol available.

3.2 Initialize the socket structure and make a socket adress.
------------------------------------------------------------------------------
After creating of the socket we have to initialize the socket structure to make
it available.
Well, here is how to do that:

struct sockaddr_in ServAddr;
const char * servIP;
int ServPort;

memset(&ServAddr, 0, sizeof(ServAddr));
ServAddr.sin_family = AF_INET;
ServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
ServAddr.sin_port = htons ( port );

This example create internet socket and accepts ALL CONNECTIONS from
ANY ADRESS. Let's have a closer look about the above lines.

This line make memset for ServAddr structure. This structure holds the server adress
and all information needed about the socket work.

memset(&ServAddr, 0, sizeof(ServAddr));

This line put the socket family. So, as i said above the socket can be internet and
local. It this example it is internet, because of that we put AF_INET.

ServAddr.sin_family = AF_INET;

The following line specifies that we accept connections from ANY adress.

ServAddr.sin_addr.s_addr = htonl(INADDR_ANY);

s_addr is variable that holds the information about the adress we agree to accept.
So, in this case i put INADDR_ANY because i would like to accept connections from any
internet adress. This case is used about server example. In a client example i
could NOT accept connections from ANY ADRESS.

After all above stuffs the next important thing is the PORT. all internet sockets
need a Input-Output PORT to make a connection. You can take any port that you want
the required condition is that port to be free. So, in other words it must be available
for us.
NOTE: Some ports need ROOT rights to opened. If this port is compromized it put
entire Operating System on risk.

So, i suggest using a NONE ROOT PORTS that are pretty much available in the computer.
These ports start from 1500 - 65536. Well, you have all these ports available for
ordinary NONE ROOT users.

Here is the example about the port initialazing.

ServAddr.sin_port = htons ( port );

Let me describe the above line. So, the "port" is a "integer variable". You can do it
in this way:

ServAddr.sin_port = htons ( 10203 );

This above line will open port 10203 for connections.

3.3 Side specific options.
----------------------------------------------------------------------------

3.3.1 Client side.
----------------------------------------------------------------------------

3.3.1.1 Connect to the server.
----------------------------------------------------------------------------
The client can be connected to the server via connect() function.
That function takes our current socket ( it is a client in that case )
and the server socket and connect them both.
Here is the example code:

connect(sock, (struct sockaddr *) &ServAddr, sizeof(ServAddr))

To be everything good and everybody to be happy do the following code:

if (connect(sock, (struct sockaddr *) &ServAddr, sizeof(ServAddr)) < 0)
printf("connect() failed\n");

This code make sure that we have available connection. If the connection faild
then the connect function wiil return -1 and the logic will print the
error message "connect() faild"
If the function succeded there is an available and ready for using connection
to the server

3.3.2 Server side
---------------------------------------------------------------------------

3.3.2.1 Binding the socket.
---------------------------------------------------------------------------
After the all preparations the next important step is socket binding. This will
bind the socket adress and the created socket. So, the adress will be connected to
the socket. If you miss this stuff you can not use your socket. Because it will
have no adress to access it. This is like the building and the street number.
If you don't know street number you could not find the building you want...

Well, here is the example how to do that:

bind ( sock, ( struct sockaddr * ) &ServAddr, sizeof ( ServAddr ) );

The bind function is very important because it will make your socket available for
using. So, better way to do above is to import some processing logic to make sure yourself
that is really available socket.

This will do binding of the socket but will check for errors and if the binding
faild ... the logic will do exit.

if ( bind ( sock, ( struct sockaddr * ) &ServAddr, sizeof ( ServAddr ) ) < 0 ){
perror ( "bind" );
exit ( EXIT_FAILURE );
}

3.3.2.2 Listening for incoming connections.
----------------------------------------------------------------------------
listen(sock, MAXPENDING);

The second argument specifies the length of the queue for pending
connections. So, if you want to use 5 pending connections you can do it in
this way:

listen (sock, 5);

This marks the socket is listening and ready to accept incoming connections.

3.3.3.3 Accepting connections.
----------------------------------------------------------------------------
The accepting of the connections goes throw some steps... First of all it needs
to make a structure with type sockaddr_in to hold client adress. After that
have to make a variable to hold the client length. Then i put the length of the client
to this variable. So, i make sure that there is enough data

Here is the example code:

struct sockaddr_in ClntAddr;
unsigned int clntLen;

clntLen = sizeof(ClntAddr);
clntSock = accept(servSock, (struct sockaddr *) &ClntAddr, &clntLen))

3.4 Transferring data.
----------------------------------------------------------------------------

3.4.1 Sending
----------------------------------------------------------------------------
When the sockets are connected the next step is just .... using of this connection. :)
Sending of data can be established via send() or write() functions.

Here is the example:

send(sock, "\n", StringLen, 0);

This is simple example how to send a "\n" character to the server. We can send
any information. Characters, symbols, any data that have to be sent...

Let me describe the above example... So, the first argument take a socket variable.
The second argument take the data that will be sent.. and the 3rd argument is an
integer variable that will specify how long is the data sended.
The last argument is for additional options, if you don't need that just
put 0 - like me. :)

NOTE:
The variable "sock" is a socket that will be used. But this is your
socket, not the socket of the server... I think this can confuse someone.
So, i assume your make a network client and for that reason you make a client socket.
That's good but throw this client socket you do all the communications.
Have a closer look and see the difference between these 2 sockets. You use the client
socket not the server one.. the server socket is for the server.

Just wanted to be clear because some people make mistake when they make a server and client
sides.

3.4.2 Receiving
----------------------------------------------------------------------------
When some data is sent from the other side someone wait to receive it...
So, this is not so hard. Here is a simple example:

read (sock, recvBuffer, 256)

The receiving is like sending - simple and easy. The first argument takes a socket
variable, the second variable takes the BUFFER for storing incoming data and the
last one takes the integer variable that specifies the length of the incoming data.

So, when you put 256 the read() function will read 256 bytes from the incomming data and
it will exit when the data is more or find the symbol "END OF DATA".

IMPORTANT:
Reserve BUFFER as the same or larger of the length you specify as read data.
DO NOT specify buffer that is smaller of the read data. If you do that you will get
"SEGMENTATION FAULT" error message and YOUR PROGRAM WILL TERMINATE.

NOTE:
The variable "sock" is a socket that will be used. But this is your
socket, not the socket of the server... I think this can confuse someone.
So, i assume your make a network client and for that reason you make a client socket.
That's good but throw this client socket you do all the communications.
Have a closer look and see the difference between these 2 sockets. You use the client
socket not the server one.. the server socket is for the server.

Just wanted to be clear because some people make mistake when they make a server and client
sides.

3.5 Advanced tricks.
----------------------------------------------------------------------------
There is a huge amount of advanced tricks in the BSD sockets...
This is the main tricks:

- Receiving data
Data receiving is important part of network socket communications. There is a issue
with the buffer when you receive some data via network if you receive data shorter than
the length of buffer data. You will receive some $%#$% datas... so this is bad.
Because of that you have to fix the received buffer and the sent data to be exactly
the same length. BUT the receiving buffer must larger +1 character than the sent data.
This is because of the last character \0 for terminating the data.
So, if you send this : "some data to be send" this is 20 length message.
Then buffer for the receiving MUST BE 20 + 1. This is because you send 20 characters,
but you receive 20+1 characters. The last one is the terminating character.

4. Full example source code.
----------------------------------------------------------------------------

4.1 network.h
----------------------------------------------------------------------------

#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>

#define ADRESS_PORT 10203
#define ADRESS_IP "127.0.0.1"
#define MAXPENDING 5
#define BUFFSIZE 21

#define SERVER_SOCKET 1
#define CLIENT_SOCKET 0

#define TRUE 1
#define FALSE 0
#define START 11
#define DIVIDER ":"

4.2 network.c
----------------------------------------------------------------------------
#include "network.h"

int make_socket ( uint16_t port, int type, const char * server_IP ){
int sock;
struct hostent * hostinfo = NULL;
struct sockaddr_in server_address;

/* Create the socket. */
sock = socket ( PF_INET, SOCK_STREAM, IPPROTO_TCP );
if (sock < 0){
perror ( "socket" );
exit ( 1 );
}

/* Give the socket a name. */
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons ( port );

if ( type == SERVER_SOCKET ){
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
if ( bind ( sock, ( struct sockaddr * ) &server_address, sizeof ( server_address ) ) < 0 ){
perror ( "bind" );
exit ( 1 );
}

if ( listen(sock, MAXPENDING) < 0 )
printf("listen() failed");
} else if ( type == CLIENT_SOCKET ){
server_address.sin_addr.s_addr = inet_addr(server_IP);

/* Establish the connection to the server */
if (connect(sock, (struct sockaddr *) &server_address, sizeof(server_address)) < 0)
printf("connect() failed\n");
}
return sock;
}

void close_socket (int socket){
close (socket);
}

char * clean_data( const char * data ){
int count;
char * ptr_data = NULL;
char * result_data = NULL;
char * temp_ptr_data = NULL;
int len;
int write_info, ifone;

ptr_data = strstr (data, DIVIDER);
ptr_data =& ptr_data[strlen(DIVIDER)];

temp_ptr_data = (char *) malloc ( strlen (ptr_data) );
strcpy (temp_ptr_data, ptr_data);
result_data = (char *) strsep (&temp_ptr_data, DIVIDER);
printf ("%i, %i, %s", strlen (data), strlen (ptr_data), result_data);
return result_data;
}

void send_data (int socket, const char * data ){
int sent_bytes, all_sent_bytes;
int err_status;
int sendstrlen;

sendstrlen = strlen ( data );
all_sent_bytes = 0;

sent_bytes = send ( socket, data, sendstrlen, 0 );
all_sent_bytes = all_sent_bytes + sent_bytes;
printf ("\t !!! Sent data: %s --- \n", data);
}

4.3 server.c
----------------------------------------------------------------------------
#include "network.h"

int accept_connection(int server_socket){
int client_socket; /* Socket descriptor for client */
struct sockaddr_in client_address; /* Client address */
unsigned int client_length; /* Length of client address data structure */

/* Set the size of the in-out parameter */
client_length = sizeof(client_address);

/* Wait for a client to connect */
if ((client_socket = accept(server_socket, (struct sockaddr *) &client_address,
&client_length)) < 0)
printf("accept() failed");

/* client_socket is connected to a client! */
printf("Handling client %s\n", inet_ntoa(client_address.sin_addr));

return client_socket;
}

void handle_client (int client_socket){
char buffer [BUFFSIZE]; /* Buffer for incomming data */
int msg_size; /* Size of received message */
int bytes, all_bytes;

do {
alarm (60);
msg_size = read (client_socket, buffer, BUFFSIZE);
alarm (0);

if ( msg_size <= 0 ){
printf ( " %i ", msg_size );
printf ( "End of data\n" );
}
} while ( msg_size > 0 );
printf ("Data received: %s", buffer);
bytes = 0;
}

int main(){
int clnt_sock;
int sock = make_socket(ADRESS_PORT, SERVER_SOCKET, "none");
clnt_sock = accept_connection (sock);
handle_client(clnt_sock);
close_socket(sock);
}

4.4 client.c
----------------------------------------------------------------------------
#include "network.h"

int main(){
int sock = make_socket(ADRESS_PORT, CLIENT_SOCKET, "10.35.43.41");
send_data (sock, "Some data to be sent");
close_socket(sock);
}


5. How to compile it?
----------------------------------------------------------------------------
Compile via cc:
Server example: cc network.c server.c -o server_example
Client example: cc network.c client.c -o client_example

To compile and use the sockets just have to include the main "include" files.
If you don't know which are they ... here you are:

Just import these lines in the begining of your program .h files.
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>

Include these and should have no problems...

6. Description.
----------------------------------------------------------------------------
BSD sockets are the base part of the networks and internet. The entire HOW-TO
is specified about the BSD socket programming but it could be used by other programmers too.
Well, the sockets are the same in all operating systems.

In the general case this HOW-TO will describe about Sockets programming in all
*NIX-like operating systems. This include Gnu/Linux, *BSD, OpenSolaris and others.

7. Copyright.
----------------------------------------------------------------------------
Copyright (c) 2006 by Svetoslav P. Chukov.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the license is included in the section entitled "GNU
Free Documentation License".



  



All times are GMT -5. The time now is 05:45 PM.

Main Menu
Advertisement
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration