What is a Socket?
Definition:
A socket is one end-point of a two-way communication link between two programs running on the network.
Stream Socket:
It provides bidirectional, reliable, sequenced, and unduplicated flow of data with no record boundaries.
Socket Creation
The socket() call creates a socket:
s= socket( domain, type, protocol);
Domain:
It is specified by one of the constants defined in <sys/socket.h>.
For the UNIX domain: AF_UNIX
For the Internet domain: AF_INET.
Type:
Socket types are defined in <sys/socket.h>. Three types are supported by AF_UNIX and AF_INET domains:
- SOCK_STREAM for stream sockets.
- SOCK_DGRAM for datagram sockets.
- SOCK_RAW for row sockets.
Protocol:
The argument 0 should be used in most cases. The system will select a protocol that supports the requested socket type.
Binding
A socket is created with no name. A remote process has no way to refer to a socket until an address is bound to it.
In the Internet domain a connection is composed of <local address, local port, foreign address foreign port>.
The bind() call allows a process to specify half of an association, such as <local address, local port>:
bind (s, name, namelen);
- s is the socket handle.
- name is a byte string that is interpreted by the supporting protocol. Internet domain names contain an Internet address and port number.
Internet binding:
struct sockaddr_in name; ... name.sin_family = AF_INET; name.sin_addr.s_addr = INADDR_ANY; name.sin_port = 0; bind (sock, (struct sockaddr *)&name, sizeof(name));
Connection Establishment
Connection establishment is usually asymmetric, with one process acting as the client and the other as the server:
The server binds a socket to a well known address.
It blocks on its socket for a connect request.
An unrelated process can connect to the server.
Listening and Communicating:
Request channel: Server do the listening.
Communication channel: Created for each connection
Client side:
On the client side the connect() call initiates a connection:
struct sockaddr_in server;
struct hostent *hp;
...
server.sin_family= AF_INET;
hp= gethostbyname( "host name");
memcpy( (char*) &server.sin_addr, (char*) hp->h_addr,
hp->h_length);
server.sin_port= htons( port_number );
...
connect( s, (struct sockaddr*) &server, sizeof server);gethostbyname() returns a structure including the network address of the specified host.
If the client's socket is unbound at the time of the connect call, the system automatically selects and bind a name to the socket.
Server side:
To receive a client's connection , a server must perform two steps after binding:
Indicate how many connections can be queued using the listen() call.
Accept the connection using the accept() call.
struct sockaddr_in from; ... listen(s, 5); fromlen= sizeof( from ); newsock= accept( s, (struct sockaddr *) &from, &fromlen);
listen() call:
s is the socket bound to the address to which the connection request is sent. The second parameter specifies the maximum number of outstanding connections that may be queued.
accept() call:
from is a structure that is filled with the address of a client. It can be a NULL pointer. accept() normally blocks.
Note:
accept() returns a new socket descriptor that is connected to the requesting client.
Data Transfer
There are several functions to send and receive data. The socket() call returns a file descriptor that can be used with many I/O Unix calls like:
write( s, buf, sizeof buf): read( s, buf, sizeof buf);
Or the calls send() and recv() can be used (they include a flag argument):
send( s, buf, sizeof buf, flags); recv( s, buf, sizeof buf, flags);
Closing Sockets
Sockets can be closed using the close() system call (because they are referenced by file descriptors).
Client Example
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define DATA "1 2 3 TESTING ... "
main (int argc, char* argv[])
{
int sock;
struct sockaddr_in server;
struct hostent *hp, *gethostbyname();
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror ("Opening stream socket");
exit (1);
}
hp = gethostbyname(argv[1]);
memcpy ((char*)&server.sin_addr, (char*) hp->h_addr, hp->h_length);
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
if ( connect(sock, (struct sockaddr *)&server, sizeof server) == -1) {
perror("Connecting stream socket");
exit(1);
}
if (write(sock, DATA, sizeof DATA) == -1)
perror("Writing data to stream socket");
close (sock);
exit (0);
} Server Example
#include
<sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1 main()
{
int sock, length; struct
sockaddr_in server;
int msgsock;
char buf[1024];
int rval;
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock==-1) {
perror( "Opening stream socket");
exit(1);
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = 0;
if (bind (sock, (struct sockaddr *)&server, sizeof(server)) == -1){
perror("Binding stream socket");
exit (1);
}
length = sizeof server;
if (getsockname (sock, (struct sockaddr *)&server,&length) == -1){
perror("Getting socket name");
exit (1);
}
printf("Socket port # %d\n", ntohs(server.sin_port));
/* Start accepting connections */
listen( sock, 5);
do {
msgsock = accept( sock, (struct sockaddr *) 0, (int*) 0);
if (msgsock==-1)
perror("accept");
else do {
memset( buf, 0, sizeof buf);
if ((rval= read( msgsock, buf, 1024)) == -1)
perror("Reading data stream");
if (rval == 0)
printf("Ending connection\n");
else
printf( "-->%s\n", buf);
} while (rval!=0);
close(msgsock);
} while (TRUE);
exit(0);
}