Windows NT Systems Programming

[ Home | Syllabus |Course Notes]


TCP/IP Client Server (Chapter 7)


TCP Connections

We focus on TCP connections only for now

Server

Client

0) Initialize winsock DLL 0) Initialize winsock DLL
1) Create Socket 1) Create Socket
2) Bind to Port 2) Connect to Port
3) Listen on Port  
4) ....Wait to Accept Connection  
5) Accept Connection ..... connected
transfer data (send/recv) transfer data

 


TCP Connection Diagram

.


WSAStartup

Initializes winsock library.

Done once at beginning of program

int WSAStartup ( 
	WORD wVersionRequested, // version requested
	LPWSADATA lpWSAData // info on version - normally ignored
);
//******* Here is an example
// MAKEWORD is a macro which combines two bytes into a 16 bit integer
// This is necessary because versions are named by two integers (e.g. 1.1 or 2.0)
WSADATA Data;

	
	WSAStartup(MAKEWORD(1, 1), &Data);
.

Creating a socket

SOCKET socket ( 
	int af, 
	int type, 
	int protocol 
); 
Parameters
af [in] An address family specification. 
	AF_INET: is the Internet family

type [in] A type specification for the new socket.
	SOCK_STREAM: for TCP connections
	SOCK_DGRAM: for UDP connections

protocol [in] A particular protocol to be used with the socket
	 that is specific to the indicated address family. 
	0 = don't care

.


Connect (client side)

Connects a socket to a particular address (machine/port)

int connect ( 
	SOCKET s, 
	const struct sockaddr FAR* name, 
	int namelen 
); 

Parameters
	
s [in] A descriptor identifying an unconnected socket. 
name [in] The name of the socket to connect to. 
namelen [in] The length of the name parameter. 

 


Naming the other end point of a connection

struct sockaddr_in {
        short   sin_family; // address family (AF_NET)
        u_short sin_port; // pick one between 1000 and 64,000
        struct  in_addr sin_addr; // IP address goes here
        char    sin_zero[8];
};
 
The Windows Sockets inet_addr  function converts
a string containing an Internet Protocol dotted address
into a proper address for the IN_ADDR structure. 

unsigned long inet_addr ( 
	const char FAR * cp 
); 

Parameters

cp [in] A null-terminated character string representing 
	a number expressed in the Internet standard ".'' notation.
 
//*** Here is an example
const int PORT = 44444;
#define DEST_IP_ADDR "127.0.0.1" // test on single machine
SOCKADDR_IN serverSockAddr;
SOCKET socket2server;
// . . .
	inet_addr(DEST_IP_ADDR);
	memcpy(&serverSockAddr.sin_addr,
		&serverAddr, sizeof(serverAddr));
	serverSockAddr.sin_port=htons(PORT);
	serverSockAddr.sin_family=AF_INET;
	
	status=connect(socket2server,
		(LPSOCKADDR) &serverSockAddr,
		sizeof(serverSockAddr));
.	

Binding a Socket to a Port (server side)

The Windows Sockets bind function associates a local address with a socket. 

int bind ( 
	SOCKET s, 
	const struct sockaddr FAR* name, 
	int namelen 
); 

Parameters

s [in] A descriptor identifying an unbound socket. 
name [in] The address to assign to the socket from the SOCKADDR structure. 
namelen [in] The length of the name. 
An example
	serverSockAddr.sin_port=htons(PORT);
	serverSockAddr.sin_family=AF_INET;
	serverSockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
	
	status=bind(serverSocket, 
		(LPSOCKADDR) &serverSockAddr,
		sizeof(serverSockAddr));
.

Listening on a port

The Windows Sockets listen function places a socket
 a state where it is listening for an incoming connection. 

int listen ( 
	SOCKET s, 
	int backlog 
); 

Parameters

s [in] A descriptor identifying a bound, unconnected socket. 
backlog [in] The maximum length of the queue of pending connections.
// AN example
	status=listen(serverSocket, 1); // allows 1 connection
.

Accepting a connection from a client

The Windows Sockets accept function accepts
 an incoming connection attempt on a socket. 

SOCKET accept ( 
	SOCKET s, 
	struct sockaddr FAR* addr, 
	int FAR* addrlen 
); 

Parameters

s [in] A descriptor identifying a socket
 that has been placed in a listening state with the listen function.
 The connection will actually be made with the socket that is returned by accept. 

Assigns an unused port number and sets up a connection.
This allows other clients to connect on the known port
// Example
	socket2client=accept(serverSocket,
		(LPSOCKADDR) &clientSockAddr,
		&addrLen);

.


Send/recv

The Windows Sockets send function sends data on a connected socket. 

int send ( 
	SOCKET s, 
	const char FAR * buf, 
	int len, 
	int flags 
); 

Parameters

s [in] A descriptor identifying a connected socket. 
buf [in] A buffer containing the data to be transmitted. 
len [in] The length of the data in buf. 
flags [in] An indicator specifying the way in which the call is made. 
Recv is similar

Putting it all together (Server Side)

SOCKET getClientSocket()
{
	WSADATA Data;
	SOCKADDR_IN serverSockAddr;
	SOCKADDR_IN clientSockAddr;
	SOCKET serverSocket;
	SOCKET socket2client;
	int addrLen=sizeof(SOCKADDR_IN);
		
	status=WSAStartup(MAKEWORD(1, 1), &Data);	// ONLY REQUIRES VERSION 1.1 OF DLL
	
	serverSocket=socket(AF_INET, SOCK_STREAM, 0);
	memset(&serverSockAddr, 0, sizeof(serverSockAddr));
	serverSockAddr.sin_port=htons(PORT);
	serverSockAddr.sin_family=AF_INET;
	serverSockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
	status=bind(serverSocket, 
		(LPSOCKADDR) &serverSockAddr,
		sizeof(serverSockAddr));
	
	status=listen(serverSocket, 1);
	
	socket2client=accept(serverSocket,
		(LPSOCKADDR) &clientSockAddr,
		&addrLen);
	return socket2client;
}

Putting it all together (client side)

const int PORT = 44444;
#define DEST_IP_ADDR "127.0.0.1" // test on single machine

	status=WSAStartup(MAKEWORD(1, 1), &Data);
	
	socket2server=socket(AF_INET, SOCK_STREAM, 0);
	
	serverAddr=inet_addr(DEST_IP_ADDR);
	memcpy(&serverSockAddr.sin_addr,
		&serverAddr, sizeof(serverAddr));
	serverSockAddr.sin_port=htons(PORT);
	serverSockAddr.sin_family=AF_INET;
	
	status=connect(socket2server,
		(LPSOCKADDR) &serverSockAddr,
		sizeof(serverSockAddr));
	

Version 2

Problem: where does the object exist:


Approach 1: Simulate Remote Procedure Calls for File WIN32 APIs

Dealing with the problems of passing parameters is called "parameter marshalling"

We will handle only the special cases of interest for version 2 at this time.

DCOM will need a more general RPC mechanism.


Packet.h

 

//**************************
// packet.h : defines packet formats between client and server
//	for Version2
// Also defines several utility functions (implemented in packet.cpp)
// These send/receive control and packet information
// and implement several WIN32 system calls as a client/server RPC
// This RPC mechanism is limited to the needs of version2
//
#ifndef PACKET_H
#define PACKET_H
#include <windows.h>
#include <winsock.h>  // also need "wsock32.lib"
#include "Message.h"

const int PORT = 44444;	// pick some arbitrary port number

typedef WORD NET_CONTROL;	// REQUEST and REPLIES sent between client/server

// REQUESTS/REPLIES:

const NET_CONTROL CREATE_FILE = 123;	// why not crazy numbers?
const NET_CONTROL READ_FILE = 234;
const NET_CONTROL WRITE_FILE = 345;
const NET_CONTROL CLOSE_HANDLE = 456;
const NET_CONTROL SUCCESS = 1;
const NET_CONTROL FAILURE = -1;
const int SIZE_NAME = 30;

// . . .
//**** ReadFile RPC section 
bool client_ReadFile(HANDLE fileHandle, LPVOID buffer, DWORD nBytes, LPDWORD nBytesOut, LPOVERLAPPED dummy);
typedef struct { // input parameters 
	HANDLE fileHandle;
	DWORD nBytesIn;
} PCKT_READ_FILE_IN;

typedef struct { // output parameters 
	NET_CONTROL status;
	BYTE buffer[SIZE_TEXT+1];
	DWORD nBytesOut;
} PCKT_READ_FILE_OUT;
// . . .
// prototype for packet send/recieve functions 

//** sendRequest - sends a request to the server
bool sendControl(SOCKET, NET_CONTROL);
NET_CONTROL getControl(SOCKET);
bool sendPacket(SOCKET, LPBYTE packet, int size);
bool getPacket(SOCKET, LPBYTE packet, int size);

Server Implementation

//		PACKET FORMATS:
// Requests consist of two packets.
// First packet is request type (which of the four procedure calls to do)
// Second Packet is the argument list for the procedure call
// Response Packet is 
//	Status (Success/Failure of RPC)
//	Return arguments
// ....
	while(TRUE) // loop until the connection goes down
	{
		request = getControl(socket2client);
		switch (request)
		{
		case CREATE_FILE:
			server_CreateFile(socket2client);
			break;
	// . . .
void server_CreateFile(SOCKET socket2client)
{
	PCKT_CREATE_FILE_IN pcktCF;
	PCKT_CREATE_FILE_OUT pcktCFout;
	HANDLE fileHandle;
	
	if(!getPacket(socket2client, LPBYTE(&pcktCF), sizeof(pcktCF))) 
	{
	// . . 
	}
	fileHandle = CreateFile(pcktCF.fileName, pcktCF.accessMode,0,0, pcktCF.createMode,0,0) ;
	pcktCFout.status = SUCCESS;
	pcktCFout.fileHandle = fileHandle; 
	if(!sendPacket(socket2client, LPBYTE(&pcktCFout), sizeof (pcktCFout))) { 
	// . . .
	}
}

Implementation of Simulated RPCs (Client Side)

//** sendControl - sends a network control word
// PRE: socket2client is ready for communications
// POST: sends "request" to socket2client
// on network failure - closes socket and cleans up TCP connection
bool sendControl(SOCKET socket2client, NET_CONTROL request)
{
	char* Ptr = (char*) &request;
	int numsnt = send(socket2client, Ptr, sizeof(request), 0);
	if (numsnt != sizeof(request)) {
	// . . 
	}
	return true;
}
//******************************************
//** sendPacket: sends packet to socket2client
bool sendPacket(SOCKET socket2client, LPBYTE packet, int size)
{
	int numsnt = send(socket2client, (char*) packet, size, 0);
	if (numsnt != size) {
	// . . .
	}
	return true;
}


bool getPacket(SOCKET socket2client, LPBYTE packet, int size)
{
	int numrcv = recv(socket2client, (char*) packet, size,0);
	if(numrcv != size)
		return false;
	return true;
}


//***********************************
//** client_WriteFile: simulates client side RPC for WriteFile
// does not support overlapped parameter
// PRE: socket2client connection is open

bool client_WriteFile(HANDLE fileHandle, LPCVOID buffer, DWORD nBytes, LPDWORD nBytesOut, LPOVERLAPPED dummy)
{
	PCKT_WRITE_FILE_IN pcktWF;
	PCKT_WRITE_FILE_OUT pcktWFout;

	if (!sendControl(socket2client, WRITE_FILE)) 
		return false;
	pcktWF.fileHandle = fileHandle; 
	strncpy((char*)(pcktWF.buffer), (char*)buffer, nBytes);
	pcktWF.nBytesIn = nBytes;
	if(!sendPacket(socket2client, LPBYTE(&pcktWF), sizeof(pcktWF))) 
		return false;
	if(!getPacket(socket2client, LPBYTE(&pcktWFout), sizeof(pcktWFout))) 
		return false;
	if(pcktWFout.status != SUCCESS)
		return false;
	*nBytesOut = pcktWFout.nBytesOut; 
	return true;
}

Changes to Object Code from Version 1

Replace File APIs with client version.

For Example:

bool CMessage::ReadMsg(HANDLE fileHandle) 
{
	DWORD nBytes;
	
	if(!client_ReadFile(fileHandle,topic,SIZE_TOPIC+1,&nBytes,0))
		return false;
	
	if(!client_ReadFile(fileHandle,text,SIZE_TEXT+1,&nBytes,0))
		return false;
	return true;
}

Copyright chris wild 1997.
For problems or questions regarding this web contact [Dr. Wild].
Last updated: September 07, 1997.