The Windows Sockets API is based on the BSD Sockets API, so many of the
calls are very similar. There are some minor differences in setup. The most
obvious differences are the requirements for header files. Listing 6.31 shows
the steps necessary to include the networking sockets functions. The Windows
header automatically includes the 1.1 ver-sion of the Windows socket library.
The #define WIN32_LEAN_AND_MEAN avoids the inclusion of this header and allows the application to
include the 2.0 version of the Windows socket library.
The Microsoft compiler also allows the source to
contain a directive indicating which libraries are to be linked into the
executable. In this case, the library is ws2_32.lib; this is more convenient than having to specify it on the command line.
We also allocate a global variable that will be used to store the handle of an
event object. The event object will be used to ensure that the server thread is
ready before the client thread sends any data.
Listing 6.31 Including
the Header Files for Windows Sockets
#ifndef WIN32_LEAN_AND_MEAN
#define
WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <process.h>
#include <winsock2.h>
#include
<stdio.h>
#pragma comment( lib, "ws2_32.lib" )
HANDLE
hEvent;
Listing 6.32 shows the code for the main thread.
The first action that the main thread needs to take is to start the Windows
sockets library with a call into WSAStartup(); this takes two parameters. The first parameter is the version number
of the library that the application requires; version 2.2 is current. The
second parameter is the address of a WSADATA structure where the description of the sockets implementation will be
stored.
Listing 6.32 The Main Thread
Is Responsible for Starting Both the Client and Server Threads
int
_tmain( int argc, _TCHAR* argv[] )
{
HANDLE serverthread, clientthread;
WSADATA
wsaData;
WSAStartup(
MAKEWORD(2,2), &wsaData );
hEvent = CreateEvent( 0, true, 0, 0 );
serverthread = (HANDLE)_beginthreadex( 0, 0, &server, 0, 0, 0 );
clientthread = (HANDLE)_beginthreadex( 0, 0, &client, 0, 0, 0 );
WaitForSingleObject( clientthread, INFINITE );
CloseHandle( clientthread );
CloseHandle( hEvent ); getchar();
WSACleanup();
return 0;
}
As previously mentioned, the code uses an event
object to ensure that the server thread starts up before the client thread
sends a request. The event is created through a call to CreateEvent(). The event is created unsignaled so that it can be signaled by the
server thread, and this signaling will enable the client thread to progress.
The event is set up to require a manual reset so that once it has been
signaled, it remains in that state. This ensures that any later client threads
will not block on the event.
The main thread then starts the server thread and the client thread
using calls to _beginthreadex(). It
waits until the client thread completes before exiting. The final
action of the main thread is to call WSACleanup() to close down the sockets library.
Listing 6.33 shows the code for the client thread. The client thread is
going to send data to the server and then print the response from the server.
The client thread first opens up a socket and then waits for the event object
to become signaled, indicating that the server is ready, before continuing.
Listing 6.33 Code
for the Client Thread
unsigned int __stdcall client( void * data )
{
SOCKET
ConnectSocket = socket( AF_INET, SOCK_STREAM, 0 );
WaitForSingleObject( hEvent, INFINITE );
struct sockaddr_in server; ZeroMemory( &server,
sizeof(server) ); server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(
"127.0.0.1" ); server.sin_port = 7780;
connect(
ConnectSocket, (struct sockaddr*)&server, sizeof(server) );
printf( "Sending 'abcd' to server\n" );
char buffer[1024];
ZeroMemory( buffer, sizeof(buffer) );
strncpy_s( buffer, 1024, "abcd", 5 );
send(
ConnectSocket, buffer, strlen(buffer)+1, 0 );
ZeroMemory( buffer, sizeof(buffer) );
recv(
ConnectSocket, buffer, 1024, 0 );
printf( "Got '%s' from server\n", buffer );
printf( "Close client\n" );
shutdown(
ConnectSocket, SD_BOTH );
closesocket(
ConnectSocket );
return 0;
}
The code uses the socket to connect to port 7780 on the localhost
(localhost is defined as the IP address 127.0.0.1). Once connected, it sends
the string "abcd" to the
server and then waits to receive a string back from the server. Once it
receives the returned string, it shuts down the connection and then closes the
socket.
Listing 6.34 shows the code for the server thread. The server thread
does not actually handle the response to any client thread. It exists only to
accept incoming connections and to pass the details of this connection onto a
newly created thread that will handle the response.
Listing 6.34 Code
for the Server Thread
unsigned
int __stdcall server( void * data )
{
SOCKET newsocket;
SOCKET ServerSocket = socket(
AF_INET, SOCK_STREAM, 0 );
struct sockaddr_in server; ZeroMemory( &server,
sizeof(server) ); server.sin_family = AF_INET; server.sin_addr.s_addr =
INADDR_ANY; server.sin_port = 7780;
bind(
ServerSocket,(struct sockaddr*)&server, sizeof(server)
); listen( ServerSocket, SOMAXCONN );
SetEvent(hEvent);
while ( (newsocket = accept(
ServerSocket, 0, 0) )!=INVALID_SOCKET )
{
HANDLE newthread;
newthread=(HANDLE)_beginthread( &handleecho, 0, (void*)newsocket);
}
printf( "Close server\n" );
shutdown(
ServerSocket, SD_BOTH );
closesocket(
ServerSocket );
return 0;
}
Listing 6.35 shows the code for the thread that
will actually respond to the client. This thread will loop around, receiving
data from the client thread and sending the same data back to the client thread
until it receives a return value that indicates the socket has closed or some
other error condition. At that point, the thread will shut down and close the
socket.
Listing 6.35 Code
for the Echo Thread
void
handleecho( void * data )
{
char buffer[1024];
int count;
ZeroMemory( buffer, sizeof(buffer) );
int socket=(int)data;
while ( (count = recv( socket,
buffer, 1023, 0) ) >0 )
{
printf( "Received %s from client\n",
buffer ); int ret = send( socket,
buffer, count, 0 );
}
printf( "Close echo thread\n" );
shutdown(
socket, SD_BOTH );
closesocket(
socket );
}
The first activity of the server thread is to open
a socket. It then binds this socket to accept any connections to port 7780. The
socket also needs to be placed into the listen state; the value SOMAXCONN contains the maximum number of connections that will be queued for
acceptance. Once these steps have been completed, the server thread signals the
event, which then enables the client thread to attempt to connect.
The main thread then waits in a
loop accepting connections until it receives a con-nection identified as INVALID_SOCKET. This will happen when the Windows socket library is shut down and is
how the server thread will exit cleanly when the other thread exits.
Every time the server thread
accepts a connection, a new thread is created, and the identification of this
new connection is passed into the newly created thread. It is impor-tant to
notice that the call to create the thread that will actually handle the work is
_beginthread(). The _beginthread() call will create a new thread that does not leave resources that need
to be cleaned up with a call to CloseHandle() when it exits. In contrast, the client and server threads were created
by the master thread with a call to _beginthreadex(), which means that they will have resources assigned to them until a
call to CloseHandle() is made.
When the loop finally receives an
INVALID_SOCKET, the
server thread shuts down and then closes the socket.
The code for Windows is sufficiently similar to
that for Unix-like operating systems that it is possible to convert between the
two. Although the example program is rela-tively simple, it illustrates the key
steps necessary for communication between two threads, two processes, or two
systems.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.