[ Home | Syllabus |Course Notes]
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 |
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); .
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
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.
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)); .
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)); .
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 .
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);
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
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; }
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));
Problem: where does the object exist:
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 : 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);
// 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))) { // . . . } }
//** 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; }
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; }