Berkeley Sockets is an application programming interface (API), which is a library for developing C applications with interprocess communication support (IPC), often used in computer networks .
Berkeley sockets (also known as the BSD socket API) first appeared as an API in the 4.1BSD Unix operating system (released in 1982) [1] . However, it was not until 1989 that the University of California at Berkeley was able to begin releasing versions of the operating system and network library without AT & T's licensing restrictions, operating in copyrighted Unix.
The Berkeley Sockets API has actually formed an abstraction standard for network sockets. Most other programming languages use an interface similar to the C language API.
The Transport Level Interface (TLI) API, based on STREAMS, is an alternative to the socket API. However, the Berkeley Sockets API predominantly prevails in popularity and number of implementations.
Berkeley Interface
The Berkeley socket interface is an API that allows you to implement interaction between computers or between processes on the same computer. This technology can work with many different input-output devices and drivers , despite the fact that their support depends on the implementation of the operating system . Such an implementation of the interface underlies TCP / IP , which makes it considered one of the fundamental technologies on which the Internet is based. Socket technology was first developed at UC Berkeley for use on UNIX systems. All modern operating systems have one or another implementation of the Berkeley socket interface, since this has become the standard interface for connecting to the Internet.
Programmers can access the socket interface at three different levels, the most powerful and fundamental of which is the level of raw sockets . A rather small number of applications need to restrict control over outgoing connections implemented by them, so support for raw sockets was intended to be available only on computers used for development based on technologies related to the Internet. Subsequently, most operating systems implemented their support, including Windows XP .
Header Files
The Berkeley Socket Program Library includes many related header files.
<sys/socket.h>- Basic BSD socket functions and data structures.
<netinet/in.h>- The PF_INET and PF_INET6 address / protocol families. Widely used on the Internet, include IP addresses, as well as TCP and UDP port numbers.
<sys/un.h>- Address family PF_UNIX / PF_LOCAL. Used for local interaction between programs running on the same computer. In computer networks does not apply.
<arpa/inet.h>- Functions for working with numeric IP addresses.
<netdb.h>- Functions for converting protocol and host names to numeric addresses. Local data is used in the same way as DNS.
Structures
sockaddris a generalized address structure, to which, depending on the protocol family used, the corresponding structure is given, for example:
struct sockaddr_in stSockAddr ;
...
bind ( SocketFD , ( const struct sockaddr * ) & stSockAddr , sizeof ( struct sockaddr_in ));
sockaddr_insockaddr_in6in_addrin6_addr
Functions
socket ()
socket() creates the connection endpoint and returns a handle . socket() takes three arguments:
- domain , indicating the protocol family of the socket to be created. This parameter specifies the naming conventions and address format. For example:
PF_INETfor IPv4 network protocol orPF_INET6for IPv6 .PF_UNIXfor local sockets (using file).
- type is one of:
SOCK_STREAMreliable thread-oriented service (TCP) (service) or streaming socketSOCK_DGRAMdatagram service (UDP) or datagram socketSOCK_SEQPACKETreliable sequential packet serviceSOCK_RAWRaw socket - raw protocol over the network layer.
- protocol determines which transport protocol to use. The most common ones are
IPPROTO_TCP,IPPROTO_SCTP,IPPROTO_UDP,IPPROTO_DCCP. These protocols are listed in <netinet / in.h>. The value “0” can be used to select the default protocol from the specified family (domain) and type (type).
The function returns −1 in case of an error. Otherwise, it returns an integer representing the assigned handle.
Prototype
#include <sys / types.h>
#include <sys / socket.h>
int socket ( int domain , int type , int protocol );
gethostbyname () and gethostbyaddr ()
The functions gethostbyname() and gethostbyaddr() return a pointer to an object of type struct hostent , describing an Internet site by name or by address, respectively. This structure contains either information received from the name server or arbitrary fields from the line in / etc / hosts. If the local name server is not running, then these routines scan / etc / hosts. Functions accept the following arguments:
- name specifying the host name. For example: www.wikipedia.org
- addr specifies a pointer to a struct in_addr containing the host address.
- len specifying the length in bytes of addr .
- type that defines the type of host address area. For example: PF_INET
Functions return a NULL pointer in case of an error. In this case, an additional integer h_errno can be checked to detect an error or an incorrect or unknown host. Otherwise, a valid struct hostent * is returned.
Prototypes
struct hostent * gethostbyname ( const char * name );
struct hostent * gethostbyaddr ( const void * addr , int len , int type );
connect ()
connect() Connects to the server. Returns an integer representing the error code: 0 means successful, and −1 indicates an error.
Some types of sockets work without establishing a connection, this mainly applies to UDP sockets. For them, the connection takes on a special meaning: the default target for sending and receiving data is assigned to the transmitted address, allowing you to use functions like send() and recv() on sockets without establishing a connection.
A loaded server may reject a connection attempt, so some types of programs need to provide for repeated connection attempts.
Prototype
#include <sys / types.h>
#include <sys / socket.h>
int connect ( int sockfd , const struct sockaddr * serv_addr , socklen_t addrlen );
bind ()
bind() binds a socket to a specific address. When a socket is created using socket() , it is associated with a certain address family, but not with a specific address. Before a socket can accept incoming connections, it must be bound to an address. bind() takes three arguments:
sockfd- a descriptor representing the socket when bindingserv_addris a pointer to asockaddrstructure representing the address to which we bind.addrlenis asocklen_tfield representing the length of thesockaddrstructure.
Returns 0 on success and −1 on error.
Prototype
#include <sys / types.h>
#include <sys / socket.h>
int bind ( int sockfd , const struct sockaddr * my_addr , socklen_t addrlen );
listen ()
listen() prepares the binding socket to accept incoming connections (the so-called "listening"). This function is applicable only to the SOCK_STREAM and SOCK_SEQPACKET socket types. Takes two arguments:
sockfdis the correct socket descriptor.backlogis an integer indicating the number of established connections that can be processed at any time. The operating system usually sets it equal to the maximum value.
After the connection is accepted, it is removed from the queue. On success, 0 is returned; in the event of an error, −1 is returned.
Prototype
#include <sys / socket.h>
int listen ( int sockfd , int backlog );
accept ()
accept() used to accept a connection request from a remote host. Takes the following arguments:
sockfdis the handle of the listening socket to accept the connection.cliaddr- a pointer to thesockaddrstructure to accept information about the client's address.addrlenis a pointer tosocklen_t, which determines the size of the structure containing the client address and passed toaccept(). Whenaccept()returns a value,socklen_tindicates how many bytes of thecliaddrstructure arecliaddruse.
The function returns the socket descriptor associated with the accepted connection, or −1 in case of an error.
Prototype
#include <sys / types.h>
#include <sys / socket.h>
int accept ( int sockfd , struct sockaddr * cliaddr , socklen_t * addrlen );
Advanced options for sockets
After creating a socket, you can set additional parameters for it. Here are some of them:
TCP_NODELAYdisables the Nagle algorithm ;SO_KEEPALIVEincludes periodic checks for 'signs of life', if supported by the OS.
Blocking and non-blocking sockets
Berkeley sockets can work in one of two modes: blocking or non-blocking. A blocking socket does not return control until it sends (or until it receives) all the data specified for the operation. This is only true for Linux systems. In other systems, such as FreeBSD, it is quite natural for a blocking socket to send not all data (but you can put the MSG_WAITALL flag in send () or recv ()). The application should check the return value to track how many bytes were sent / received and, accordingly, resend the currently unprocessed information [2] . This can lead to problems if the socket continues to “listen”: the program may hang due to the fact that the socket is waiting for data that may never arrive.
A socket is usually indicated as blocking or nonblocking using the fcntl() or ioctl() functions.
Data Transfer
To transfer data, you can use the standard read / write functions of the read and write files, but there are special functions for transmitting data through sockets:
- send
- recv
- sendto
- recvfrom
- sendmsg
- recvmsg
It should be noted that when using the TCP protocol (sockets of the SOCK_STREAM type) there is a chance to receive less data than was transmitted, since not all the data have yet been received, so you need to either wait until the recv function returns 0 bytes, or set the MSG_WAITALL flag function recv , which will make it wait for the transfer to end. For other types of sockets, the MSG_WAITALL flag MSG_WAITALL not change anything (for example, in UDP the entire packet = the whole message). See also the chapter "Blocking and non-blocking sockets."
Releasing Resources
The system does not release the resources allocated by the call to socket() until the call close() occurs. This is especially important if the connect() call failed and can be repeated. Each socket() call must have a corresponding close() call in all possible execution paths. You must add the header file <unistd.h> to support the close function.
The result of the close() system call is only a call to the interface to close the socket, and not close the socket itself. This is the command for the kernel to close the socket. Sometimes on the server side, a socket can go on standby TIME_WAIT for up to 4 minutes. [one]
Sample client and server using TCP
TCP implements the concept of a connection. The process creates a TCP socket by calling the socket() function with the PF_INET or PF_INET6 , as well as SOCK_STREAM (Stream Socket) and IPPROTO_TCP .
Server
Creating a simple TCP server consists of the following steps:
- Creating TCP sockets by calling the
socket()function. - Binding a socket to a listening port by calling the
bind()function. Before callingbind()programmer must declare asockaddr_instructure, clear it (usingmemset()), thensin_family(PF_INETorPF_INET6) and fill in thesin_portfields (listened port, specified as a sequence of bytes ). The conversion ofshort intto byte order can be performed by calling thehtons()function (short for “from host to network”). - Preparing a socket for listening for connections (creating a listening socket) by calling
listen(). accept()incoming connections viaaccept(). This blocks the socket until an incoming connection is received, and then returns a socket descriptor for the accepted connection. The initial descriptor remains a descriptor listened, andaccept()can be called again for this socket at any time (as long as it is open).- A connection to a remote host that can be created using
send()andrecv()orwrite()andread(). - The final closure of each open socket that is no longer needed occurs with
close(). It should be noted that if there were anyfork()calls, then each process should close its known sockets (the kernel keeps track of the number of processes that have an open handle), and in addition, two processes should not use the same socket at the same time.
/ * C server code * /
#include <sys / types.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define port 1100
int main ( void ) {
struct sockaddr_in stSockAddr ;
int i32SocketFD = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP );
if ( i32SocketFD == - 1 ) {
perror ( "error creating socket" );
exit ( EXIT_FAILURE );
}
memset ( & stSockAddr , 0 , sizeof ( stSockAddr ));
stSockAddr . sin_family = PF_INET ;
stSockAddr . sin_port = htons ( port );
stSockAddr . sin_addr . s_addr = htonl ( INADDR_ANY );
if ( bind ( i32SocketFD , ( struct sockaddr * ) & stSockAddr , sizeof ( stSockAddr )) == - 1 ) {
perror ( "Error: bind" );
close ( i32SocketFD );
exit ( EXIT_FAILURE );
}
if ( listen ( i32SocketFD , 10 ) == - 1 ) {
perror ( "Error: Listening" );
close ( i32SocketFD );
exit ( EXIT_FAILURE );
}
for (;;) {
int i32ConnectFD = accept ( i32SocketFD , 0 , 0 );
if ( i32ConnectFD < 0 ) {
perror ( "Error: Accept " );
close ( i32SocketFD );
exit ( EXIT_FAILURE );
}
/ * read and write operations ... * /
shutdown ( i32ConnectFD , SHUT_RDWR );
close ( i32ConnectFD );
}
return 0 ;
}
Customer
Creating a TCP client is as follows:
- Creating a TCP socket by calling
socket(). - Connect to the server using
connect(), passing thesockaddr_instructure withsin_familywith the specifiedPF_INETorPF_INET6,sin_portto specify the listening port (in byte order), andsin_addrto specify the IPv4 or IPv6 address of the server being listened to (also in byte order). - Interaction with the server using
send()andrecv()orwrite()andread(). - End a connection and reset information when you call
close(). Similarly, if there were anyfork()calls, each process must close the (close()) socket.
/ * Client code in C * /
#include <sys / types.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main ( void ) {
struct sockaddr_in stSockAddr ;
int i32Res ;
int i32SocketFD = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP );
if ( i32SocketFD == - 1 ) {
perror ( "Error: unable to create socket" );
return EXIT_FAILURE ;
}
memset ( & stSockAddr , 0 , sizeof ( stSockAddr ));
stSockAddr . sin_family = PF_INET ;
stSockAddr . sin_port = htons ( 1100 );
i32Res = inet_pton ( PF_INET , "192.168.1.3" , & stSockAddr . sin_addr );
if ( i32Res < 0 ) {
perror ( "Error: the first parameter does not belong to the category of valid addresses" );
close ( i32SocketFD );
return EXIT_FAILURE ;
} else if ( ! i32Res ) {
perror ( "Error: the second parameter does not contain a valid IP address" );
close ( i32SocketFD );
return EXIT_FAILURE ;
}
if ( connect ( i32SocketFD , ( struct sockaddr * ) & stSockAddr , sizeof ( stSockAddr )) == - 1 ) {
perror ( "Error: Connections" );
close ( i32SocketFD );
return EXIT_FAILURE ;
}
/ * read and write operations ... * /
shutdown ( i32SocketFD , SHUT_RDWR );
close ( i32SocketFD );
return 0 ;
}
Sample client and server using UDP
UDP is based on a connectionless protocol, that is, a protocol that does not guarantee the delivery of information. UDP packets may not arrive in the specified order, be duplicated and arrive more than once, or even not reach the addressee at all. Because of these minimal guarantees, UDP is significantly inferior to TCP. The absence of a connection means the absence of streams or connections between two hosts, since instead data arrives in datagrams ( datagram socket ).
UDP address space, the area of UDP port numbers (in ISO terminology - TSAP) is completely separated from TCP ports.
Server
The code can create a UDP server on port 7654 as follows:
int sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP );
struct sockaddr_in sa ;
int bound ;
ssize_t recsize ;
socklen_t * address_len = NULL ;
sa . sin_addr . s_addr = htonl ( INADDR_ANY );
sa . sin_port = htons ( 7654 );
bound = bind ( sock , ( struct sockaddr * ) & sa , sizeof ( struct sockaddr ) );
if ( bound < 0 )
fprintf ( stderr , "bind (): error% s \ n " , strerror ( errno ) );
bind () binds a socket to an address / port pair.
while ( 1 )
{
printf ( "recv test .... \ n " );
recsize = recvfrom ( sock , ( void * ) Hz , 100 , 0 , ( struct sockaddr * ) & sa , address_len );
if ( recsize < 0 )
fprintf ( stderr , "Error% s \ n " , strerror ( errno ) );
printf ( "recsize:% d \ n " , recsize );
sleep ( 1 );
printf ( "datagram:% s \ n " , Hz );
}
Such an infinite loop receives all UDP datagrams arriving on port 7654 using recvfrom () . The function uses the parameters:
- socket
- pointer to data buffer
- buffer size
- flags (similar to reading or other socket receive functions),
- sender address structure
- The length of the sender's address structure.
Customer
The simplest demonstration of sending a UDP packet containing “Hello!” To the address 127.0.0.1, port 7654, looks like this:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys / socket.h>
#include <sys / types.h>
#include <netinet / in.h>
#include <unistd.h> / * to call close () for a socket * /
int main ( void )
{
int sock ;
struct sockaddr_in sa ;
int bytes_sent ;
const char * buffer = "Hello!" ;
int buffer_length ;
buffer_length = strlen ( buffer ) + 1 ;
sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP );
if ( sock == - 1 )
{
printf ( "Error creating socket" );
return 0 ;
}
sa . sin_family = PF_INET ;
sa . sin_addr . s_addr = htonl ( 0x7F000001 );
sa . sin_port = htons ( 7654 );
bytes_sent =
sendto (
sock
buffer ,
strlen ( buffer ) + 1 ,
0 ,
( struct sockaddr * ) & sa ,
sizeof ( struct sockaddr_in )
);
if ( bytes_sent < 0 )
printf ( "Error sending package:% s \ n " , strerror ( errno ) );
close ( sock );
return 0 ;
}
See also
- Computer network
- Internet socket
- UNIX domain socket
- Winsock is an API based on Berkeley sockets, but intended to create distributed applications on the Microsoft Windows platform .
Notes
- ↑ Uresh Vahalia. UNIX internals: the new frontiers. - Upper Saddle River, New Jersey 07458: Prentice Hall PTR, 2003. - 844 p. - ISBN 0-13-101908-2 .
- ↑ Beej's Guide to Network Programming
Links
The de jure definition of the sockets interface is contained in the POSIX standard, better known as:
- IEEE Std. 1003.1-2001 Standard for Information Technology - Portable Operating System Interface (POSIX).
- Open Group Technical Standard: Base Specifications, Issue 6, December 2001.
- ISO / IEC 9945: 2002
- Austin's website - information about these standards, as well as current work on them.
- RFC3493 and RFC3542 describe the IPv6 extension of the underlying socket API.
- Unix Manual Pages
- "Learn the algorithms of TCP system calls" (rus.) On the IBM website.
- Kaspersky K. "Tutorial games for WINSOCK"
- Beej's Guide to Network Programming - 2007
- UnixSocket FAQ
- Get system IP list - C ++ Example
- quick TCP-IP NetIntro with C examples
- Porting Berkeley Socket programs to Winsock - Microsoft's documentation.
- Programming UNIX Sockets in C - Frequently Asked Questions - 1996
- Linux network programming - Linux Journal , 1998