Search This Blog

Thursday, January 12, 2012

Linux Socket Programming In C++



LINUX GAZETTE

"Linux Gazette...making Linux just a little more fun!"





Linux Socket Programming In C++

By Rob Tougher




Contents

1. Introduction
2. Overview of Client-Server Communications
3. Implementing a Simple Server and Client
3.1 Server - establishing a listening socket
3.2 Client - connecting to the server
3.3 Server - Accepting the client's connection attempt
3.4 Client and Server - sending and receiving data
4 Compiling and Testing Our Client and Server
4.1 File list
4.2 Compile and test
5. Conclusion

1. Introduction

Sockets are a mechanism for exchanging data between processes. These processes can either be on the same machine, or on different machines connected via a network. Once a socket connection is established, data can be sent in both directions until one of the endpoints closes the connection.
I needed to use sockets for a project I was working on, so I developed and refined a few C++ classes to encapsulate the raw socket API calls. Generally, the application requesting the data is called the client, and the application servicing the request is called the server. I created two primary classes, ClientSocket and ServerSocket, that the client and server could use to exchange data.
The goal of this article is to teach you how to use the ClientSocket and ServerSocket classes in your own applications. We will first briefly discuss client-server communications, and then we will develop a simple example server and client that utilize these two classes.

2. Overview of Client-Server Communications

Before we go jumping into code, we should briefly go over the set of steps in a typical client-server connection. The following table outlines these steps:
Server Client
1. Establish a listening socket and wait for connections from clients.
2. Create a client socket and attempt to connect to server.
3. Accept the client's connection attempt.
4. Send and receive data. 4. Send and receive data.
5. Close the connection. 5. Close the connection.
That's basically it. First, the server creates a listening socket, and waits for connection attempts from clients. The client creates a socket on its side, and attempts to connect with the server. The server then accepts the connection, and data exchange can begin. Once all data has been passed through the socket connection, either endpoint can close the connection.

3. Implementing a Simple Server and Client

Now its time to dig into the code. In the following section we will create both a client and a server that perform all of the steps outlined above in the overview. We will implement these operations in the order they typically happen - i.e. first we'll create the server portion that listens to the socket, next we'll create the client portion that connects to the server, and so on. All of the code in this section can be found in simple_server_main.cpp and simple_client_main.cpp.
If you would rather just examine and experiment with the source code yourself, jump to this section. It lists the files in the project, and discusses how to compile and test them.

3.1 Server - establishing a listening socket

The first thing we need to do is create a simple server that listens for incoming requests from clients. Here is the code required to establish a server socket:
listing 1 : creating a server socket ( part of simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the server socket
      ServerSocket server ( 30000 );

      // rest of code -
      // accept connection, handle request, etc...

    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
    }

  return 0;
}


That's all there is to it. The constructor for the ServerSocket class calls the necessary socket APIs to set up the listener socket. It hides the details from you, so all you have to do is create an instance of this class to begin listening on a local port.
Notice the try/catch block. The ServerSocket and ClientSocket classes use the exception-handling feature of C++. If a class method fails for any reason, it throws an exception of type SocketException, which is defined in SocketException.h. Not handling this exception results in program termination, so it is best to handle it. You can get the text of the error by calling SocketException's description() method as shown above.

3.2 Client - connecting to the server

The second step in a typical client-server connection is the client's responsibility - to attempt to connect to the server. This code is similar to the server code you just saw:
listing 2 : creating a client socket ( part of simple_client_main.cpp )
#include "ClientSocket.h"
#include "SocketException.h"
#include 
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the client socket
      ClientSocket client_socket ( "localhost", 30000 );

      // rest of code -
      // send request, retrieve reply, etc...

    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\n";
    }

  return 0;
}


By simply creating an instance of the ClientSocket class, you create a linux socket, and connect it to the host and port you pass to the constructor. Like the ServerSocket class, if the constructor fails for any reason, an exception is thrown.

3.3 Server - accepting the client's connection attempt

The next step of the client-server connection occurs within the server. It is the responsibility of the server to accept the client's connection attempt, which opens up a channel of communication between the two socket endpoints.
We have to add this functionality to our simple server. Here is the updated version:
listing 3 : accepting a client connection ( part of simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the socket
      ServerSocket server ( 30000 );

      while ( true )
 {
   ServerSocket new_sock;
   server.accept ( new_sock );

   // rest of code -
   // read request, send reply, etc...

 }
    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
    }

  return 0;
}

Accepting a connection just requires a call to the accept method. This method accepts the connection attempt, and fills new_sock with the socket information about the connection. We'll see how new_sock is used in the next section.

3.4 Client and Server - sending and receiving data

Now that the server has accepted the client's connection request, it is time to send data back and forth over the socket connection.
An advanced feature of C++ is the ability to overload operators - or simply, to make an operator perform a certain operation. In the ClientSocket and ServerSocket classes I overloaded the << and >> operators, so that when used, they wrote data to and read data from the socket. Here is the updated version of the simple server:
listing 4 : a simple implementation of a server ( simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the socket
      ServerSocket server ( 30000 );

      while ( true )
 {

   ServerSocket new_sock;
   server.accept ( new_sock );

   try
     {
       while ( true )
  {
    std::string data;
    new_sock >> data;
    new_sock << data;
  }
     }
   catch ( SocketException& ) {}

 }
    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
    }

  return 0;
}

The new_sock variable contains all of our socket information, so we use it to exchange data with the client. The line "new_sock >> data;" should be read as "read data from new_sock, and place that data in our string variable 'data'." Similarly, the next line sends the data in 'data' back through the socket to the client.
If you're paying attention, you'll notice that what we've created here is an echo server. Every piece of data that gets sent from the client to the server gets sent back to the client as is. We can write the client so that it sends a piece of data, and then prints out the server's response:
listing 5 : a simple implementation of a client ( simple_client_main.cpp )
#include "ClientSocket.h"
#include "SocketException.h"
#include 
#include 

int main ( int argc, int argv[] )
{
  try
    {

      ClientSocket client_socket ( "localhost", 30000 );

      std::string reply;
      try
 {
   client_socket << "Test message.";
   client_socket >> reply;
 }
      catch ( SocketException& ) {}

      std::cout << "We received this response from the server:\n\"" << reply << "\"\n";;

    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\n";
    }

  return 0;
}
We send the string "Test Message." to the server, read the response from the server, and print out the response to std output.

4. Compiling and Testing Our Client And Server

Now that we've gone over the basic usage of the ClientSocket and ServerSocket classes, we can build the whole project and test it.

4.1 File list

The following files make up our example:
Miscellaneous:
Makefile - the Makefile for this project
Socket.h, Socket.cpp - the Socket class, which implements the raw socket API calls.
SocketException.h - the SocketException class
Server:
simple_server_main.cpp - main file
ServerSocket.h, ServerSocket.cpp - the ServerSocket class
Client:
simple_client_main.cpp - main file
ClientSocket.h, ClientSocket.cpp - the ClientSocket class

4.2 Compile and Test

Compiling is simple. First save all of the project files into one subdirectory, then type the following at your command prompt:
prompt$ cd directory_you_just_created
prompt$ make

This will compile all of the files in the project, and create the simple_server and simple_client output files. To test these two output files, run the server in one command prompt, and then run the client in another command prompt:
first prompt:
prompt$ ./simple_server
running....



second prompt:
prompt$ ./simple_client
We received this response from the server:
"Test message."
prompt$
The client will send data to the server, read the response, and print out the response to std output as shown above. You can run the client as many times as you want - the server will respond to each request.

5. Conclusion

Sockets are a simple and efficient way to send data between processes. In this article we've gone over socket communications, and developed an example server and client. You should now be able to add socket communications to your applications!


Rob Tougher

Rob is a C++ software engineer in the NYC area. When not coding on his favorite platform, you can find Rob strolling on the beach with his girlfriend, Nicole, and their dog, Halley.

Sockets Client - Server Model

Subsections

                                                                        LINK

IPC:Sockets

Sockets provide point-to-point, two-way communication between two processes. Sockets are very versatile and are a basic component of interprocess and intersystem communication. A socket is an endpoint of communication to which a name can be bound. It has a type and one or more associated processes.
Sockets exist in communication domains. A socket domain is an abstraction that provides an addressing structure and a set of protocols. Sockets connect only with sockets in the same domain. Twenty three socket domains are identified (see <sys/socket.h>), of which only the UNIX and Internet domains are normally used Solaris 2.x Sockets can be used to communicate between processes on a single system, like other forms of IPC.
The UNIX domain provides a socket address space on a single system. UNIX domain sockets are named with UNIX paths. Sockets can also be used to communicate between processes on different systems. The socket address space between connected systems is called the Internet domain.
Internet domain communication uses the TCP/IP internet protocol suite.
Socket types define the communication properties visible to the application. Processes communicate only between sockets of the same type. There are five types of socket.

A stream socket
-- provides two-way, sequenced, reliable, and unduplicated flow of data with no record boundaries. A stream operates much like a telephone conversation. The socket type is SOCK_STREAM, which, in the Internet domain, uses Transmission Control Protocol (TCP).
A datagram socket
-- supports a two-way flow of messages. A on a datagram socket may receive messages in a different order from the sequence in which the messages were sent. Record boundaries in the data are preserved. Datagram sockets operate much like passing letters back and forth in the mail. The socket type is SOCK_DGRAM, which, in the Internet domain, uses User Datagram Protocol (UDP).
A sequential packet socket
-- provides a two-way, sequenced, reliable, connection, for datagrams of a fixed maximum length. The socket type is SOCK_SEQPACKET. No protocol for this type has been implemented for any protocol family.
A raw socket
provides access to the underlying communication protocols.
These sockets are usually datagram oriented, but their exact characteristics depend on the interface provided by the protocol.

Socket Creation and Naming

int socket(int domain, int type, int protocol) is called to create a socket in the specified domain and of the specified type. If a protocol is not specified, the system defaults to a protocol that supports the specified socket type. The socket handle (a descriptor) is returned. A remote process has no way to identify a socket until an address is bound to it. Communicating processes connect through addresses. In the UNIX domain, a connection is usually composed of one or two path names. In the Internet domain, a connection is composed of local and remote addresses and local and remote ports. In most domains, connections must be unique.
int bind(int s, const struct sockaddr *name, int namelen) is called to bind a path or internet address to a socket. There are three different ways to call bind(), depending on the domain of the socket.

  • For UNIX domain sockets with paths containing 14, or fewer characters, you can:
    #include 
     ... 
    bind (sd, (struct sockaddr *) &addr, length);
    
  • If the path of a UNIX domain socket requires more characters, use:
    #include  
    ... 
    bind (sd, (struct sockaddr_un *) &addr, length); 
    
  • For Internet domain sockets, use
    #include 
    ... 
    bind (sd, (struct sockaddr_in *) &addr, length);
    
In the UNIX domain, binding a name creates a named socket in the file system. Use unlink() or rm () to remove the socket.

Connecting Stream Sockets

Connecting sockets is usually not symmetric. One process usually acts as a server and the other process is the client. The server binds its socket to a previously agreed path or address. It then blocks on the socket. For a SOCK_STREAM socket, the server calls int listen(int s, int backlog) , which specifies how many connection requests can be queued. A client initiates a connection to the server's socket by a call to int connect(int s, struct sockaddr *name, int namelen) . A UNIX domain call is like this:

struct sockaddr_un server; 
... 
connect (sd, (struct sockaddr_un *)&server, length);
while an Internet domain call would be:
struct sockaddr_in; 
... 
connect (sd, (struct sockaddr_in *)&server, length);
If the client's socket is unbound at the time of the connect call, the system automatically selects and binds a name to the socket. For a SOCK_STREAM socket, the server calls accept(3N) to complete the connection.
int accept(int s, struct sockaddr *addr, int *addrlen) returns a new socket descriptor which is valid only for the particular connection. A server can have multiple SOCK_STREAM connections active at one time.

Stream Data Transfer and Closing

Several functions to send and receive data from a SOCK_STREAM socket. These are write(), read(), int send(int s, const char *msg, int len, int flags), and int recv(int s, char *buf, int len, int flags). send() and recv() are very similar to read() and write(), but have some additional operational flags.
The flags parameter is formed from the bitwise OR of zero or more of the following:

MSG_OOB
-- Send "out-of-band" data on sockets that support this notion. The underlying protocol must also support "out-of-band" data. Only SOCK_STREAM sockets created in the AF_INET address family support out-of-band data.
MSG_DONTROUTE
-- The SO_DONTROUTE option is turned on for the duration of the operation. It is used only by diagnostic or routing pro- grams.
MSG_PEEK
-- "Peek" at the data present on the socket; the data is returned, but not consumed, so that a subsequent receive operation will see the same data.
A SOCK_STREAM socket is discarded by calling close().

Datagram sockets

A datagram socket does not require that a connection be established. Each message carries the destination address. If a particular local address is needed, a call to bind() must precede any data transfer. Data is sent through calls to sendto() or sendmsg(). The sendto() call is like a send() call with the destination address also specified. To receive datagram socket messages, call recvfrom() or recvmsg(). While recv() requires one buffer for the arriving data, recvfrom() requires two buffers, one for the incoming message and another to receive the source address.
Datagram sockets can also use connect() to connect the socket to a specified destination socket. When this is done, send() and recv() are used to send and receive data.
accept() and listen() are not used with datagram sockets.

Socket Options

Sockets have a number of options that can be fetched with getsockopt() and set with setsockopt(). These functions can be used at the native socket level (level = SOL_SOCKET), in which case the socket option name must be specified. To manipulate options at any other level the protocol number of the desired protocol controlling the option of interest must be specified (see getprotoent() in getprotobyname()).

Example Socket Programs:socket_server.c,socket_client

These two programs show how you can establish a socket connection using the above functions.

socket_server.c

#include 
#include 
#include 
#include 

#define NSTRS       3           /* no. of strings  */
#define ADDRESS     "mysocket"  /* addr to connect */

/*
 * Strings we send to the client.
 */
char *strs[NSTRS] = {
    "This is the first string from the server.\n",
    "This is the second string from the server.\n",
    "This is the third string from the server.\n"
};

main()
{
    char c;
    FILE *fp;
    int fromlen;
    register int i, s, ns, len;
    struct sockaddr_un saun, fsaun;

    /*
     * Get a socket to work with.  This socket will
     * be in the UNIX domain, and will be a
     * stream socket.
     */
    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        perror("server: socket");
        exit(1);
    }

    /*
     * Create the address we will be binding to.
     */
    saun.sun_family = AF_UNIX;
    strcpy(saun.sun_path, ADDRESS);

    /*
     * Try to bind the address to the socket.  We
     * unlink the name first so that the bind won't
     * fail.
     *
     * The third argument indicates the "length" of
     * the structure, not just the length of the
     * socket name.
     */
    unlink(ADDRESS);
    len = sizeof(saun.sun_family) + strlen(saun.sun_path);

    if (bind(s, &saun, len) < 0) {
        perror("server: bind");
        exit(1);
    }

    /*
     * Listen on the socket.
     */
    if (listen(s, 5) < 0) {
        perror("server: listen");
        exit(1);
    }

    /*
     * Accept connections.  When we accept one, ns
     * will be connected to the client.  fsaun will
     * contain the address of the client.
     */
    if ((ns = accept(s, &fsaun, &fromlen)) < 0) {
        perror("server: accept");
        exit(1);
    }

    /*
     * We'll use stdio for reading the socket.
     */
    fp = fdopen(ns, "r");

    /*
     * First we send some strings to the client.
     */
    for (i = 0; i < NSTRS; i++)
        send(ns, strs[i], strlen(strs[i]), 0);

    /*
     * Then we read some strings from the client and
     * print them out.
     */
    for (i = 0; i < NSTRS; i++) {
        while ((c = fgetc(fp)) != EOF) {
            putchar(c);

            if (c == '\n')
                break;
        }
    }

    /*
     * We can simply use close() to terminate the
     * connection, since we're done with both sides.
     */
    close(s);

    exit(0);
}

socket_client.c


#include 
#include 
#include 
#include 

#define NSTRS       3           /* no. of strings  */
#define ADDRESS     "mysocket"  /* addr to connect */

/*
 * Strings we send to the server.
 */
char *strs[NSTRS] = {
    "This is the first string from the client.\n",
    "This is the second string from the client.\n",
    "This is the third string from the client.\n"
};

main()
{
    char c;
    FILE *fp;
    register int i, s, len;
    struct sockaddr_un saun;

    /*
     * Get a socket to work with.  This socket will
     * be in the UNIX domain, and will be a
     * stream socket.
     */
    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        perror("client: socket");
        exit(1);
    }

    /*
     * Create the address we will be connecting to.
     */
    saun.sun_family = AF_UNIX;
    strcpy(saun.sun_path, ADDRESS);

    /*
     * Try to connect to the address.  For this to
     * succeed, the server must already have bound
     * this address, and must have issued a listen()
     * request.
     *
     * The third argument indicates the "length" of
     * the structure, not just the length of the
     * socket name.
     */
    len = sizeof(saun.sun_family) + strlen(saun.sun_path);

    if (connect(s, &saun, len) < 0) {
        perror("client: connect");
        exit(1);
    }

    /*
     * We'll use stdio for reading
     * the socket.
     */
    fp = fdopen(s, "r");

    /*
     * First we read some strings from the server
     * and print them out.
     */
    for (i = 0; i < NSTRS; i++) {
        while ((c = fgetc(fp)) != EOF) {
            putchar(c);

            if (c == '\n')
                break;
        }
    }

    /*
     * Now we send some strings to the server.
     */
    for (i = 0; i < NSTRS; i++)
        send(s, strs[i], strlen(strs[i]), 0);

    /*
     * We can simply use close() to terminate the
     * connection, since we're done with both sides.
     */
    close(s);

    exit(0);
}

Exercises

Exercise 12776
Configure the above socket_server.c and socket_client.c programs for you system and compile and run them. You will need to set up socket ADDRESS definition.


Dave Marshall
1/5/1999

Programming in C UNIX System Calls and Subroutines using C


 Programming in C
UNIX System Calls and Subroutines using C.







Search for Keywords in C Notes


Keyword Searcher


Download Postscript Version of Notes


Click Here to Download Course Notes. Local Students Only.



Algorithm Animations


Direct link to Java Algorithm Animations (C related)




C COURSEWARE

Lecture notes + integrated exercises, solutions and marking

 

Online Marking of C Programs --- CEILIDH

Introduction to network functions in C


Introduction to network functions in C

LINK


Contents







Introduction

In this tutorial, I will attempt to explain the use and syntax of some of the basic UNIX networking functions in C. If you want to know more about Windows Sockets programming, I'm afraid the WinSock resources at StarDust have disappeared now - any pointers to similar pages would be appreciated!

You might also want to check out the
Internet programming crash course. You could also visit Networking ABC Everything Networking, from 2 computer home networking to XP networking.

There are some example programs which I will explain how to write from the beginning. If you would like to know more about a function, you might have on-line manual pages (use 'man function') or your system administrators may be able to provide manuals.

You can view or download the source code but I cannot guarantee that it will work on all systems so if you find any code that doesn't work, please email me with details of your system (Operating System, Architecture etc) and what doesn't work and I'll see if I can fix it.
I would also appreciate email telling me if it DOES work on your system (or if you managed to modify parts and get it working), if you could email the alterations to me, I can then incorporate your modifications into the source code.
Some platforms that have been tested:
  • Linux, kernel 2.2.x, gcc2
  • Linux, kernel 2.4.x, gcc3
  • Mac OSX 10.3 - references to malloc.h need to be changed to sys/malloc.h and as well as 'ar', you also need to run 'ranlib' on the library file libnet.a (built from the network library source)
  • HPUX - need to add -lsocket when linking executable as mentioned below.
With some versions of UNIX, the example programs will not link, complaining about things such as undefined symbols bind, accept, listen and socket.
Such errors will probably look like this:
Undefined                       first referenced
 symbol                             in file
socket                              /var/tmp/ccLkjMcu.o
accept                              /var/tmp/ccLkjMcu.o
bind                                /var/tmp/ccLkjMcu.o
listen                              /var/tmp/ccLkjMcu.o
ld: fatal: Symbol referencing errors. No output written to a.out
This generally indicates that you should link in an additional library by adding -lsocket to the cc line.
For example: cc -o netapp netapp.c -lsocket

This tutorial assumes you know how to program in C.
If you find this tutorial of any interest whatsoever, please contact me and let me know. It's nowhere near complete as yet, but mail me anyway even if it's only to say "Hey! get that tutorial finished" :-)
Back to contents



Creating a socket

The first thing to do is to create a socket which you can then manipulate in many ways. To create a socket, you can use the socket() function.
Includes:
#include 
#include  
Syntax:
int socket(int af, int type, int protocol);
C source:
int socket_desc;

  socket_desc=socket(AF_INET,SOCK_STREAM,0);
  if (socket_desc==-1)
    perror("Create socket");
socket() returns a socket descriptor which can be used in other network commands. This will create a socket which uses DARPA Internet addresses, and the method of connection is a byte stream which is similar to a pipe. An alternative to byte streams is a datagram but this tutorial does not cover them.
With servers, the first socket created is often known as a " master socket". Before the socket can send or receive data, it must be connected to another socket. If acting as a master socket, it must be bound to a port number so that clients can know where to "find" the socket and connect to it.
If successful, socket() returns a valid socket descriptor; otherwise it returns -1 and sets errno to indicate the error. perror() or strerror() can be used to turn the errno value into a human readable string.
Back to contents



Binding a socket to a port

To set up a master socket, you need to bind the socket descriptor to a in many ways. To create a socket, you can use the socket() function as described above in Creating a socket.
Includes:
#include 
#include 
Syntax:
int bind(int s, struct sockaddr *addr, int addrlen);
C source:
struct sockaddr_in address;

/* type of socket created in socket() */
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = INADDR_ANY;
/* 7000 is the port to use for connections */
  address.sin_port = htons(7000);
/* bind the socket to the port specified above */
  bind(socket_desc,(struct sockaddr *)&address,sizeof(address));
If successful, bind() returns 0; otherwise it returns -1 and sets errno to indicate the error.
The port specified in the source code above (port 7000) is where the server can be connected to. To test this, compile the program `sockbind' from the source code directory and run it. While it is running, type:
telnet localhost 7000
and you should get
Trying...
for a few seconds and then
telnet: Unable to connect to remote host: Connection refused
as the server program finishes. This indicates the server was ok. If the connection is refused immediately, there is probably a problem with the server.
Back to contents



Listening for connections

Before any connections can be accepted, the socket must be told to listen for connections and also the maximum number of pending connections using listen()
Includes:
#include 
Syntax:
int listen(int s, int backlog);
C source:
  listen(socket_desc,3);
the above line specifies that there can be up to 3 connections pending. If a connection request arrives when there are already 3 connections pending, the client receives a timeout error.
listen() applies only to unconnected sockets of type SOCK_STREAM. If the socket has not been bound to a local port before listen() is invoked, the system automatically binds a local port for the socket to listen on.
If successful, listen() returns 0; otherwise it returns -1 and sets errno to indicate the error.
Back to contents



Accepting a connection

To actually tell the server to accept a connection, you have to use the function accept()
Includes:
#include 
Syntax:
int accept(int s, struct sockaddr *addr, int *addrlen);
C source:
int addrlen;
struct sockaddr_in address;

  addrlen = sizeof(struct sockaddr_in);
  new_socket = accept(socket_desc, (struct sockaddr *)&address, &addrlen);
  if (new_socket<0)
    perror("Accept connection");
accept() is used with connection based sockets such as streams. The parameters are the socket descriptor of the master socket followed by a sockaddr_in structure and the size of the structure. If successful, accept() returns a positive integer which is the socket descriptor for the accepted socket. If an error occurs, -1 is returned and errno is set to indicate the cause.
There is some example source code in the directory, the program to compile is called `accept' While it is running, type:
telnet localhost 7000
and you should get
Trying...
Connected to localhost.
Escape character is '^]'.
and then 10 seconds later, it should close the connection. Once again, if the connection is refused immediately, there is probably a problem with the server.
Back to contents



Closing connections

Probably one of the easiest things to do with a socket, is close it. This is done using close()
Includes:
#include 
Syntax:
int close(int sockdes);
C source:
  close(new_socket);
close() closes the socket descriptor indicated by sockdes.
Upon successful completion, close() returns a value of 0; otherwise, it returns -1 and sets errno to indicate the error.
Back to contents



Sending data to a connection

Accepting a connection would not be any use without the means to send or receive data. Send without receive could be used for an information server which always returns a fixed message.
Includes:
#include 
Syntax:
int send(int s, const void *msg, int len, int flags);
C source:
char *message="This is a message to send\n\r";

  send(new_socket,message,strlen(message),0);
The message should have \n\r instead of just \n or \r because otherwise the text which appears on some clients may seem strange. e.g the text with just a \n would appear as follows:
This is a message
                 and this is the second line
                                            and the third.
instead of:
This is a message
and this is the second line
and the third.
send() is used to transmit a message to another socket and can be used only when the socket is in a connected state. The socket descriptor that specifies the socket on which the message will be sent is 's' in the syntax above. 'msg' points to the buffer containing the message and the length of the message is given by len, in bytes.
The supported values for flags are zero, or
MSG_OOB (to send out-of-band data) - a write() call made to a socket behaves in exactly the same way as send() with flags set to zero.
Upon successful completion, send() returns the number of bytes sent. Otherwise, it returns -1 and sets errno to indicate a locally-detected error. The `accept' program is modified to send a welcome message to the connection before it closes the socket. To see how this is done, have a look at the source code for the program `send'.
Back to contents



Receiving data from a connection

Accepting a connection would not be any use without the means to send or receive data. Receive only could be used as a data collection method.
Includes:
#include 
Syntax:
int recv(int s, void *msg, int len, int flags);
C source:
int bufsize=1024;        /* a 1K buffer */
char *buffer=malloc(bufsize);

  recv(new_socket,buffer,bufsize,0);
The flags parameter can be set to MSG_PEEK, MSG_OOB, both, or zero. If it is set to MSG_PEEK, any data returned to the user still is treated as if it had not been read, i.e the next recv() re-reads the same data.
A read() call made to a socket behaves in exactly the same way as a recv() with flags set to zero.
If successful, recv() returns the number of bytes received, otherwise, it returns -1 and sets errno to indicate the error. recv() returns 0 if the socket is blocking and the connection to the remote node failed.
Back to contents



Setting socket options

To allow certain socket operations requires manipulation of socket options using setsockopt()
Includes:
#include 
Syntax:
  int setsockopt(int s, int level, int optname,
                 const void *optval, int optlen);
C source:
#define TRUE   1
#define FALSE  0

int socket_desc;     /* master socket returned by socket() */
int opt=TRUE;        /* option is to be on/TRUE or off/FALSE */

  setsockopt(socket_desc,SOL_SOCKET,SO_REUSEADDR,
              (char *)&opt,sizeof(opt));
SOL_SOCKET specifies the option is a `socket level' option, these are defined in The socket is identified by the socket descriptor s.
The option SO_REUSEADDR is only valid for AF_INET sockets.
There are two kinds of options: boolean and non-boolean. Boolean options are either set or not set and also can use optval and optlen to pass information. Non-boolean options always use optval and optlen to pass information.
Back to contents



Handling more than one connection

To enable a socket to be read without waiting if there is no input, the socket must be set non-blocking using the following snippet of code.
fcntl(mastersocket, F_SETFL, FNDELAY);
or
fcntl(mastersocket, F_SETFL, O_NONBLOCK);
If the above returns a non-zero result, the operation failed and errno should be set to an appropriate value.
Using select to monitor a number of sockets (or just one) is fairly straightforward and is shown in the code below. Please note this is incomplete code as the creation of the master socket is not included (see previous details).
fd_set readfds;

/* create a list of sockets to check for activity */
FD_ZERO(&readfds);

/* specify mastersocket - ie listen for new connections */
FD_SET(mastersocket, &readfds);

/* wait for connection, forever if have to */
new_conns=select(max_conns, readfds, NULL, NULL, NULL);

if ((new_conns<0) && (errno!=EINTR)) {
  /* there was an error with select() */
}
if (FD_ISSET(mastersocket,&readfds)) {
  /* Open the new socket */
}
Of course, the above will only wait for activity on the master socket. What you need to do is run it inside a loop which repeats until the server is shut down.
Any newly created sockets will need to be monitored as well (unless the connections accepted are closed after outputing a message).
For an example of this, see the sample program multi.c which accepts up to three connections and relays data from one socket to the others. It's almost a very basic chat server.
Back to contents



Converting a hostname into a network address

Includes:
#include 
Syntax:
  struct hostent *gethostbyname(const char *name);

C source:
  struct hostent *hent;

  hent = gethostbyname("www.foobar.net");

A hostent structure:
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 */
}
Some of the network functions require a structure containing the network address and sometimes the port number to connect to or from. The easiest way to convert a hostname to a network address is to use the gethostbyname() function.
gethostbyname() returns a structure of type hostent - this structure contains the name of the host, an array of alternative names and also an array of network addresses (in network byte order).
Back to contents



Establishing an outgoing connection

To establish a connection to another socket (similar to telnet), use the function connect().
#include 
#include 

int  connect(int  sockfd, struct sockaddr *serv_addr, int addrlen );
Create the socket using socket(), convert the hostname to an IP address using gethostbyname() and then issue the connect() call passing the relevant structures containing the IP address and port to connect to.
  struct hostent     *he;
  struct sockaddr_in  server;
  int                 sockfd;

/* resolve localhost to an IP (should be 127.0.0.1) */
  if ((he = gethostbyname("localhost")) == NULL) {
    puts("error resolving hostname..");
    exit(1);
  }

/*
 * copy the network address part of the structure to the 
 * sockaddr_in structure which is passed to connect() 
 */
  memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
  server.sin_family = AF_INET;
  server.sin_port = htons(7000);

/* connect */
  if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) {
    puts("error connecting..");
    exit(1);
  }
Using connect() ... more to come when/if I get around to it.