Advertisment

Socket Programming in Linux

author-image
PCQ Bureau
New Update

From word processors to spreadsheets, databases to

games, an in-creasing number of applications are becoming network-aware. So how do these

programs communicate with each other? What does your Web browser do when it wants to send

a request to the local Internet server? While playing a death match in Quake, how do the

various Quake machines talk to each other?

Advertisment

Berkeley Sockets is what most programs use to

communicate. For instance, when your Web browser opens a connection to the remote HTTP

server, it uses Sockets. Whenever you click a link on a Web page, the browser sends the

HTTP request through this connection. For that matter, every Quake client uses sockets to

communicate with a Quake server.

In this article, we’ll take a look at socket

programming and along the way we’ll build a small client/server application using the

Berkeley Sockets API.

The programs given here have been written and compiled in

Linux, using the Gnu C compiler, gcc. We’ve used Linux since socket is primarily a

Unix API. The Windows series of operating systems use a derivative of the Berkeley sockets

called Windows Sockets, popularly called Winsocks.

Advertisment

What are sockets? COLOR="#000000">

A socket allows a software program ( face="Arial" size="2">process) to communicate with other processes over a network.

Usually one process waits for others to connect to it. This process is called the server.

The connecting processes are clients.

Servers usually come in two forms: iterative or concurrent.

An iterative server gives its entire attention to a client. Other clients wanting to

connect to the server have to wait. Only when the server finishes with the first client

does it service the others.

Advertisment

A concurrent server is capable of handling more than one

client at a time. When a client requests a connection, the server spawns a copy of itself

called a child.

The child then handles the client while the parent (the

server) waits for new connections. Whenever a new client comes along, the server spawns

another child to handle the request.

Different kinds of sockets COLOR="#000000">

Advertisment

There are various types of sockets.

For instance, take two processes running on the same (Unix) system. They can use Unix

Domain Protocol sockets to communicate. These sockets can communicate with other processes

on the same system only. If a process wishes to communicate over the Internet, then it

uses Internet Domain Protocol
sockets.

Moreover, a socket can be connection-oriented or

connection-less. When the client and server processes maintain a dedicated link between

themselves, they use connection-oriented sockets. Think of it as a telephone conversation,

where the two parties talking to each other maintain a link throughout.

The connection-less socket is like the postal service. The

client sends a packet of information called a datagram to the server. The server receives

the datagram sometime later and sends a reply to the client.

Advertisment

What is a port? COLOR="#000000">

A port is a 16-bit number used by a server to identify

itself to the clients. Imagine that there are three different servers running on a host.

Each network interface on the host has its own IP address. The client comes along through

one of these interfaces. How does this client decide on which server to contact? The

client can resolve this conflict if it knows the port number of the server it wants to

reach.

Several Internet services (servers) are so popular that

they have been given their own port numbers. The FTP server, for instance, always uses the

port number 21. Whenever an FTP client wants to connect to an FTP server, it connects on

port 21. The Internet community has reserved ports 1 to 1,023 for standard services like

telnet, finger, and FTP. The rest of them have been left free for other user-developed

servers.

Advertisment

The best way to understand the socket architecture is to

see it in action. Look in the accompanying PC Quest CD \cdrom\ sourcecode

directory for the file, server.txt. This file contains installation instructions

for a small client/server program we’ll examine in this article. The server presented

here is concurrent and opens a connection-oriented Internet domain socket for each client

that it services.

The server COLOR="#000000">

Let us examine how the server works.

Look at the file
server.c.

Advertisment

if ((sd = socket(AF_INET, SOCK_STREAM, 0))

< 0)



err("server: unable to open socket.");

The line opens a socket. Since we want to open an Internet

Domain Protocol socket, we use the AF_INET define. AF_INET stands for Address Family

Internet.

A connection-oriented socket is also known as a stream

socket since the data is sent in streams. This is opposed to packets of information

(datagrams) sent in connection-less sockets. Here, the SOCK_STREAM defined is used since

we want a connection-oriented socket.

The socket() system call returns an integer by which we can

later identify this socket. This integer is called a socket descriptor. The server then

binds this socket with a port using the bind() system call. We’ve used port number

5,523 for our server. This is an arbitrary number; any unassigned port will do.

if (bind(sd, (struct sockaddr *)

&server_addr,



sizeof(server_addr)) < 0)


err("server: Unable to bind local address.");

Okay, now that we have opened a socket and bound it to a

port, its time to receive connections from clients. The server tells the system that

it’s ready for any clients with the listen() system call.

listen(sd, 5);

Next, the server enters an infinite loop and waits for an

actual connection with a client. Whenever a client connects, the accept() system call,

returns a new socket descriptor with the same properties as the original socket.

newsd = accept(sd, (struct sockaddr *)

&client_addr, &client_size);

A child process is then spawned by the server using the

fork() system call. The child uses the new socket descriptor to communicate with the

client.

if ((cpid = fork()) < 0)



err("server: unable to spawn child process.");


else if (cpid == 0) /* CHILD PROCESS */


{


close(sd); /* close the old socket */


if ((readval = read(newsd, buf, sizeof(buf))) < 0)


err("server: unable to read from socket.");


...


...






Meanwhile, the server waits for other clients. If another

client wants to connect, the server spawns a new child process to handle it.
COLOR="#ff0000">

The client COLOR="#000000">

Now that we have seen how the server

works, let’s have a look at a client. The file
client.c

implements a small client program that connects to our server. The client also opens a

socket using the socket() system call.

Meanwhile, the server is waiting for clients on port 5,523.

The client requests a connection with our server using the connect() system call.

if (connect(sd, (struct sockaddr *)

&server_addr,



sizeof(server_addr)) < 0)


err("client: unable to connect to server.");

If the client connects without any snags, it sends a simple

string across to the server and terminates.

char message<> = "Hello World";



...


...

if (write(sd, message, sizeof(message))

< 0)



err("client: unable to write to server.");

The server receives this string from the client and prints

it on the screen.

That’s all that’s there to our simple

client/server implementation. Feel free to modify the code. Try writing a more

sophisticated system, a small time server for instance. The time server

returns the correct time of day to any connecting client. There are some wonderful books

around dealing with network programming. Unix Network Programming by W Richard

Stevens is an old classic that I recommend highly.

Advertisment