Chapter: Multicore Application Programming For Windows, Linux, and Oracle Solaris : Using POSIX Threads

Sockets

A particular advantage of using sockets for communication is that scaling beyond a single system becomes a relatively minor change to the code.

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.


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail
Multicore Application Programming For Windows, Linux, and Oracle Solaris : Using POSIX Threads : Sockets |


Privacy Policy, Terms and Conditions, DMCA Policy and Compliant

Copyright © 2018-2024 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.