Sockets
A full treatment of socket
programming is beyond the scope of this book. However, sockets remain an
important way that processes or threads can communicate, so it is worth
examining the use of sockets for communication within the same system. A
particular advantage of using sockets for communication is that scaling beyond
a single system becomes a relatively minor change to the code.
The use of sockets is different from other
communication mechanisms because the client that opens the socket has different
responsibilities than the server connected to the socket. So, it is appropriate
to tackle the client and server as two entirely separate applications.
The first thing that any process that uses sockets
has to do is request a socket. The socket is a potential connection to the
network. The call to socket() takes
three parameters: the family of socket being requested, the type of socket
within that family, and the protocol. The protocol should usually be set to
zero to indicate that the default protocol should be used. The protocol family
should be AF_INET or AF_INET6, and the protocol type for these two families is either SOCK_STREAM for TCP/IP or SOCK_DGRAM for
UDP/IP.
Once a socket has been established, it is necessary
to connect it to an address. We can best illustrate this by initially working
through the code necessary to write a server process that waits for a
connection from a client, before discussing the code that would be found in
such a client. This makes sense because the two scenarios have little code in
common.
A server will call bind() with the address of the local host and the port on which it will listen
for connections. The function bind() takes as
parameters the previously established socket, a pointer to a structure
containing the details of the address to bind to, the value INADDR_ANY is typically used for this, and the size of the structure. The
par-ticular structure that needs to be used will depend on the protocol, which
is why it is passed by the pointer.
Once a server has been bound to an address, the
server can then call listen() on the
socket. The parameters to this call are the socket and the maximum number of
queued connections as a parameter.
After a call to listen, the server can wait for a
connection from a client by calling accept(). The parameters to the accept() call are
the socket, an optional pointer to a socket address structure, and the size of the structure. If the pointer
to the socket address structure is not zero, the call to accept() will write details of the client into the socket address structure. The
call will return a new socket descriptor that is the connection to the client.
The server can read from or write to the new socket
until the connection is termi-nated by either the client or the server calling close() on the new socket.
Data can be sent through the socket using either
the write() call or
the send() call.
The send() call
affords some additional flexibility in sending data. Similarly, data can be
received from the socket using either the read() or recv() call. A
socket that has been closed is indicated by a return of an error from the
calls.
The code in Listing 5.72 shows the part of the server code that handles
echoing data back. While there is still data in the socket, the thread will
read that data, write it to stdout, and
then echo it back to the socket where it came from. One complexity is that
we do not want the server process to call pthread_join() for every thread that it cre-ates to handle an incoming connection. To
avoid the call to pthread_join(), each
thread immediately calls pthread_detach() once it has
been created. This detaches the thread and ensures that any resources that the
thread uses are returned to the process when the thread terminates.
Listing 5.72 Code
for Echo Server Thread
#include <pthread.h> #include <stdio.h>
#include <unistd.h>
void
* handleecho( void * param )
{
char buffer[1024]; int count;
pthread_detach( pthread_self() ); int s =
(int)param;
while ( count = read( s, buffer,
1023 ) > 0 )
{
buffer[count] = 0;
printf( "Received %s \n", buffer ); write( s, buffer, count );
}
close( s );
}
The more interesting part of the code is the code
to set up the server, as shown in Listing 5.73. The server sets up a socket and
binds this to port 7779. It also configures the socket to hold a queue of up to
five connections. The server then listens on the socket for incoming
connections. When a connection arrives, the server creates a new thread to
handle this connection and passes the socket number as a parameter to the new
thread. The resulting application needs to be linked with the socket library (-lsocket) and the network services library (-lnsl).
Listing 5.73 Code
to Set Up the Server
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
int
main()
{
int newsocket;
int s = socket( AF_INET,
SOCK_STREAM, 0 ); // TCP/IP socket
struct
sockaddr_in server;
bzero(
&server, sizeof(server) ); // Clear
address structure
server.sin_family
= AF_INET; // TCP/IP family
server.sin_addr.s_addr = INADDR_ANY; // Any
address
server.sin_port = 7779; // Port
to bind to
bind(
s, (struct sockaddr*)&server, sizeof(server) ); listen( s, 5 ); // Queue of
five outstanding connections
while
( newsocket = accept( s, 0, 0 ) )
{
pthread_t
thread;
pthread_create(
&thread, 0, handleecho, (void*) newsocket );
}
}
Listing 5.74 shows the client code. In a similar way to the server, the
client sets up a socket. However, the client calls connect() with the address and port of the system that it wants to connect to.
Once the call to connect()
completes, the client can start sending data to the server and receiving data
back from the server using the socket. In Listing 5.74, the client sends a
string to the server and then prints out the data returned by the server.
Listing 5.74 Client
Code That Sends Data to a Server and Prints Response
#include <sys/types.h> #include
<sys/socket.h> #include <strings.h> #include <arpa/inet.h>
#include <unistd.h> #include <stdio.h>
int main()
{
int s =
socket( AF_INET, SOCK_STREAM, 0 ); // TCP/IP socket
struct sockaddr_in server;
bzero( &server, sizeof(server) ); //
Clear address structure
server.sin_family = AF_INET; //
TCP/IP family
server.sin_addr.s_addr = inet_addr( "127.0.0.1" );
// Local machine
server.sin_port = 7779; // Port to bind to
if ( connect( s, (struct sockaddr*)&server,
sizeof(server) ) == 0 )
{
printf( "Sending 'abcd' to server\n" ); char buffer[1024];
strncpy( buffer, "abcd", 4 );
write( s,
buffer, strlen(buffer) );
int count = read( s, buffer, 1024
);
buffer[count] = 0;
printf( "Got %s from server\n", buffer );
shutdown( s,
SHUT_RDWR );
}
}
Sockets represent a convenient way of communicating
between a number of inde-pendent processes that are spread over one or more
systems. They are a good approach if it is expected that the application will
end up scaling beyond a single system.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2024 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.