You are here

TutorialLibraryTest ApplicationDownload
[Tutorial]LibraryTest ApplicationDownload

Socket Tutorial

Chapter 5 - Description of low level socket functions

In the previous chapter, we saw the special function select, which allows us to implement the concept of timeout in our library. Before continuing, let's first go around the other socket manipulation functions that we will use later.

  • gethostname
  • gethostbyname
  • socket
  • closesocket
  • bind
  • listen
  • accept
  • connect
  • send
  • recv




Function gethostname

Function prototype is the following:

int gethostname(
   char* name,
   int namelen
);
  • name : [out] pointer to a buffer for reception local computer name.
  • namelen : [in] buffer length in Bytes;
  • Valeur de retour : 0 if no error.




Function gethostbyname

Function prototype is the following:

struct hostent* FAR gethostbyname(
   const char* name
);
  • name : [in] computer name from which we want to get network information.
  • Valeur de retour : pointer to a hostent structure. This structure is filled with the requested information. Null in case of error.

Structure hostent is defined as follows :

typedef struct hostent {
   char FAR* h_name;
   char FAR  FAR** h_aliases;
   short h_addrtype;
   short h_length;
   char FAR  FAR** h_addr_list;
} hostent;
  • h_name : Computer name.
  • h_aliases : Array of alternative string names. Last item is a null pointer;
  • h_addrtype : Type of address used by the machine.. For example, AF_INET corresponds to IPv4 addresses ; AF_INET6 corresponds to IPv6 addresses.
  • h_length : Length in Bytes of every single item in the h_addr_list array.
  • h_addr_list : Array containing the different addresses of the computer. Last item is a null pointer. Macroth_addr is defined as h_addr_list[0].




Function socket

This function creates a socket by defining the type of address and protocol. It prototype is the following :

int socket(
   int addr_family,
   int type,
   int protocol
);
  • addr_family : [in] Address family to use with the socket (AF_INET -> IP version 4, AF_INET6 -> IP version 6, etc.). In this tutorial we only use the value AF_INET.
  • type : [in] Connection type. Typical values ​​are SOCK_STREAM for the TCP protocol and SOCK_DRGRAM for the UDP protocol. In this tutorial we will use the value SOCK_STREAM.
  • protocol : [in] Protocol to use. Possible values depend on the address family and on the type. If value is set to 0, the protocol is chosen automatically. Considering our options, it will be TCP/IP.
  • Valeur de retour : -1 in case of error (Linux), or INVALID_SOCKET (Windows). In the other case, the value is a valid socket identifier.




Function closesocket (Windows) / close (Linux)

This function frees the resources used by a socket. It must be called for each successful call to the socket function. The prototype is:

int closesocket / close(
   int socket,
);
  • socket : [in] Socket identifier we want to free. All possible pending operations (reading, writing, etc.) are cancelled.
  • Valeur de retour : 0 if no error.




Function bind

We have seen that function socket enables to create a socket. However, it is not tied to any particular address, only the type of address and protocol to be used have been defined. Such socket is so called unnamed. Function bind can bind a unnamed socket to a specific address. It should be used on a socket on which we are preparing to listen and accept client connections (see function listen).

Function prototype is the following:

int bind(
   int socket,
   const struct sockaddr* name,
   int namelen
);
  • socket : [in] Socket identifier we want to bind to an address.
  • name : [in] Pointer to a structure describing this address.
  • namelen : [in] Length in Bytes of this structure.
  • Valeur de retour : 0 if no error.

The right sockaddr structure to use for parameter name actually depends on the protocol used. In the case of IP version 4, we must use the sockaddr_in structure:

struct sockaddr_in {
   short   sin_family;
   u_short sin_port;
   struct  in_addr sin_addr;
   char    sin_zero[8];
};
  • sin_family : Address type. Here we only use AF_INET.
  • sin_port : Port on which the socket must be bound (for example, the listening port for a TCP/IP server). Attention: the value must be written in Network Byte Order (equivalent to Big-Endian) ; see function htonl which ensures that.
  • sin_addr : Address to which the socket must be bound. For a TCP/IP server, it is the local IP address. Otherwise it may be the address of the remote socket on which to connect. Parameter in_addr is an union of 4 Bytes encoding an IPv4 address (example "192.168.11.16"). For a server, this field can take the special value INADDR_ANY, which means "any local computer address". Otherwise it is possible to write the value of the h_addr_list field form the hostent structure (see function gethostbyname). It is also possible to transform a literal IP address string thanks to the functioninet_addr.

Here is an example of different ways to fill in this structure in the case of a socket that is prepared to listen for incoming connections on port 6969:

struct sockaddr_in sa;
struct hostent *hp;

// Method 1 :
hp = gethostbyname ("localhost");
sa.sin_family = hp->h_addrtype;
sa.sin_port = htons (6969);
memcpy ((char*) &sa.sin_addr, hp->h_addr, hp->h_length);

// Method 2 :
sa.sin_family = AF_INET;
sa.sin_port = htons (6969);
sa.sin_addr.s_addr = INADDR_ANY;

// Method 3 :
sa.sin_family = AF_INET;
sa.sin_port = htons (6969);
sa.sin_addr.s_addr = inet_addr("127.0.0.1");




Function listen

This function puts the socket in a special listening state, waiting for connection requests. It is only used by connection-oriented sockets (which is the case of TCP/IP) and bind must have been previously called. The function returns immediately. Each connection request must then be accepted individually tanks to function accept.

int listen(
   int socket,
   int backlog
);
  • socket : [in] Socket identifier we want to put in the listening state.
  • backlog : [in] Maximum number of clients that may be waiting for their connection request to be accepted (see function accept). Once this number is reached in the wait queue, following connection requests are directly rejected.
  • Return value : 0 if no error.




Function accept

This feature allows to accept the next connection request in the pending queue. It is only used by connection-oriented sockets (which is the case of TCP / IP) and listen must have been previously called. The function can block (indefinitely) if the socket is in blocking mode and if no client is waiting for connection establishment. If the socket is non-blocking and there is no connection request, the function returns an error. Otherwise a new socket is created and its identifier is returned by the function. This new socket becomes the interface to use in order to communicate with this new client. The initial socket is not modified and continues in listening mode.

Function prototype is the following :

int accept(
   int socket,
   struct sockaddr* addr,
   int* addrlen
);
  • socket : [in] socket identifier in listening mode on which a connection request must be accepted.
  • addr : [out] pointer to an optional sockaddr structure (in our case it is a sockaddr_in structure) in which socket address for the new connection is written.
  • addrlen : [in, out] optional pointer to the size of the addr structure.The value is changed after function call to reflect the number of bytes written.
  • Return value : INVALID_SCOKET (Windows) or-1 (Linux) in case of error. Otherwise, a valid socket identifier for the new connection.




Function connect

This feature enables to connect to a remote socket. It is only used by connection-oriented sockets (which is the case of TCP / IP). If the socket is in blocking mode, the return value of the function indicates whether the connection occurred or not. In non-blocking mode, the function returns immediately and the information of success or failure may be retrieved tanks to the function select, which lets you know when the operation is complete.

Function prototype is the following :

int connect(
   int socket,
   const struct sockaddr* name,
   int namelen
);
  • socket : [in] socket identifier we want to connect to a remote socket.
  • name : [in] pointer to a sockaddr structure (in our case it is a sockaddr_in structure) containing information about the address of the remote socket.
  • addrlen : [in] size of the name structure in Bytes.
  • Return value : 0 if no error.

See function bind for more information on structure sockaddr.




Function send

This function sends data through a connection-oriented socket (which is the case of TCP/IP). The connection must have already been established (see function connect or accept). The function can block if the socket's internal buffers are full and can not immediately accept the message. Then, the function will return as soon as the buffers have been emptied or the socket connection has been interrupted. In non-blocking mode, function returns immediately, but it is possible that data have not all been taken into account.

Function prototype is the following:

int send(
   int socket,
   const char* buf,
   int len,
   int flags
);
  • socket : [in] socket identifier to use to send data.
  • buf : [in] pointer to the data to send.
  • len : [in] data length in Bytes.
  • flags : [in] 0 for normal sending.
  • Return value :SOCKET_ERROR (Windows) or -1 (Linux) in case of any error. Otherwise, function returns the number of Bytes actually written in the transmission buffer (this value can be less than parameter len for a non-blocking socket).




Function recv

This function enables to receive data from a connection-oriented socket (which is the case of TCP/IP). The connection must have already been established (see function connect or accept). The function can block if the socket's internal buffers do not contain much data that the amount requested. Then, the function will return as soon as the required number of bytes is available or if the socket connection has been interrupted. In non-blocking mode, function returns immediately, but it is possible that the number of data received is less than the amount requested.

Function prototype is the following:

int recv(
   int socket,
   char* buf,
   int len,
   int flags
);
  • socket : [in] socket identifier to use to receive data.
  • buf : [out] pointer to a buffer for data reception.
  • len : [in] size of the buffer in Bytes.
  • flags : [in] 0 for normal reception.
  • Return value :SOCKET_ERROR (Windows) or -1 (Linux) in case of any error. Otherwise, function returns the amount of Bytes actually received (this value can be less than parameter len for a non-blocking socket).