Windows NT Systems
Programming
[ Home
| Syllabus |Course Notes]
TCP/IP Client Server (Chapter 7)
TCP Connections
We focus on TCP connections only for now
- TCP connections are made between two sockets
(one of each end of the connection)
- A socket allows bi-directional communications
- From the network side, sockets are known by
their port number
- A machine is known by its IP address (e.g
128.82.4.1)
- IP addresses are unique throughout the world
- A name server will translate symbolic
machines names (e.g. pitfall.cs.odu.edu) into IP
addresses
- IP address 127.0.01 is a specific IP address
- always the machine you are on
- TCP connections are done asymmetrically
- A Server announces its intention to
accept connections
- A Client can connect to a waiting
server
- A client's attempt to connect to a port not
being served will fail
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
- TCP (like a
telephone call)
- Stream
- Reliable
- Ordered
- Point to
point connection
- Can be
routed
- UDP (like e-mail)
- Discrete
(DataGrams)
- Not
guaranteed
- Arbitrary
ordering allowed
- Can be
broadcast to many machines
- Limited
to a single network
.
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
- Identifying which procedure
call
- Sending input parameters/
Receiving output parameters
- Types
- Translating between
different type representations if necessary
- Dereferencing Pointers
- Passing Dynamic Data
Structures (those with embedded pointers)
- Call by value, call by
reference, call by value in/out
void foo(int &x, int &y)
foo(a,a);
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 03, 1997.