Programming internet tcp/ip in Win32 environment for C coders By DoxtorL. April 2004-December 2004, version 1.2. I) Introduction. II) What is a window in win32 environment? III) A simple, window based, program example. IV) client/server model. V) A brief overview of some functions of Win32 winsock library. VI) Examples of client, server sources code: Example 1: Example of a simple TCP/IP client. Example 2: Example of a simple TCP/IP server. Example 3: Example of a simple TCP/IP server, capable to serve several clients at once. I) introduction Lately, reading some sources or dissassembly of popular network worms like my- doom,bagle,i have noticed most of these worms are coded by people who probably code only console mode applications, they are surely "Linux gurus". But Windows isn't Linux! The paradigm of a program running in Win32 environment is different from the one in Linux: Windows programming relies on the concept of window! Linux do- esn't rely on it. II) What is a window in win32 environment? A window is a basic object in Win32 environment, you don't have to confuse it with the concept of GUI (graphic user interface), these two concepts are a bit different! A window object doesn't need to be visible by users at all! In Win32 environment, the OS sends messages to applications again and again to notify them about occuring events (a key has been pressed, data has been recei- ved on a TCP/IP socket,...) to be able to be notified by such events, an appli- cation needs to use an window object, otherwise the application is like a blind man! Attached to a window object, you have three important things: 1) A message queue Here is a graphic to figure it out: <-start of the list of messages to be processed by the application msg1 msg2 msg3 msg4 ... msgN <- messages are added by OS when a message is processed, the message is removed from the queue. 2) The message loop It's a kind of "infinite loop". Messages are read and sent to the "window pro- cedure" (read below).If a message to terminate the program has been received the program exits the loop and it continues, usually to terminate, it's the programmer's choice. 3) A routine to process the messages received, "the window procedure". In fact, it's a sort of "case of": You can think of it as being something like this: switch(msg) { case MSG1: break; case MSG2: break; ... case MSG: break; default: } MSG1,MSG2,...,MSG are either some Windows or user defined constants. Windows has a set of defined events, but users can add some events. The handlers are user defined, they are the code the programmer wants to be executed in response to a message received from the OS. III) A simple window based program example. The syntax to program an application using a window object can seem a bit comp- lex and confusing at first sight because, to handle the window procedure, Win- dows uses the concept of "callback" function. what's that? To call a function usually you have to pass parameters or pointers. Sometime, in Win32 API (application programming interface) you need to pass a pointer to another function to use special functions.These functions are called "callback". It isn't your program that calls these functions directly but Win32 API. Anyway, we don't care, we only care about the use of a window object in a Windows program, so a simple working skeleton is all we need. Here is such skeleton for a Win32 program using a window object: /********** start of source code*/ #include LRESULT CALLBACK WndProcedure(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam); /* window class*/ WNDCLASSEX wc={sizeof(WNDCLASSEX),0,WndProcedure,0,0,0,0,0,0,0,"vclass",0}; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,\ int nCmdShow) { MSG Msg; wc.hInstance=hInstance; /*register the window class just created:*/ RegisterClassEx(&wc); /*create the window:*/ if(CreateWindowEx(0,"vclass",0,0,0,0,0,0,0,0,hInstance,0)) { /*message loop*/ while( GetMessage(&Msg,NULL,0,0)) { DispatchMessage(&Msg); } } /*exit:*/ return 0; } /*callback function*/ LRESULT CALLBACK WndProcedure(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam) { switch(Msg) { /*in this example, we are only interested in processing the message notifying the program is about to be destroyed*/ case WM_DESTROY: MessageBox(0,"BYE!","WARNING",0); PostQuitMessage(0); break; default: /*let Windows handle other messages*/ return DefWindowProc(hWnd,Msg,wParam,lParam); } return 0; } /********* End of the source code*/ You are probably wondering "what the heck this program is doing ?", "Is it run- ning fine?","I don't see anything!" Yes, it's running fine and even if it uses a window object, you cannot see a window appearing on your desktop. If you don't have already killed this pro- gram in memory, kill it! You will soon discover what it's doing. IV) client/server model Alot of applications programmed for Internet use are using this model. For example, when you use a FTP (file transfer protocol) client, this model is behind the programs involved in retrieving/sending a file. The client part is involved in requesting a file to a server The server part is involved in sending the file requested by the client. The procedures involved in the transfer are as follow: 1) The client, knowing the IP address (internet protocol address) of the server and the PORT used by the server to run this service, tries to CONNECT to the server and waits for a response. 2) The Server ACCEPTs the incoming connection and creates an new communication interface to be used to serve the client. The communication interface used to ACCEPT the connection is freed and ready to ACCEPT a new incoming connection. 3) The client SENDs a request (name of the file to get, for example). 4) The server RECEIVES the request and SENDs back the file asked. 5) When the client has got all it wanted,it DISCONNECTs the connection to the server. The communication interface used is closed. Here the steps usually performed by a client: A) Create a communication interface. B) CONNECT to a given IP address/PORT C) Wait for a response from the server. D) If the Server has ACCEPTED the CONNECTION, the client SENDs requests. E) RECEIVE all packets sent by the server F) When the client wants to quit, DISCONNECT the CONNECTION. Here the steps usually performed by a Server: A) Create a communication interface. B) BIND it to a given PORT. C) LISTEN to wait for incoming connections D) If an incoming connection is arriving, ACCEPT it and create a new communica- tion interface to serve the new client. E) RECEIVE the request from the client. F) SEND data requested by the client. G) When the client DISCONNECTs, close the communication interface created to serve the client. Do not confuse RECEIVE and LISTEN! V) A brief overview of some functions of Win32 winsock library I don't want to make a description of all winsock library functions. We only need few of them: accept(),bind(),closesocket(),connect(),listen(), recv(),send(),socket(). and the three Windows specific functions: WSAStartup(),WSACleanup(),WSAAsyncSelect() 1) WSAStartup(),WSACleanup() Before being able to use the winsock functions the first thing to do is to call WSAStartup(). Prototype: int PASCAL FAR WSAStartup(WORD wVersionRequired,LPWSADATA lpWSAData); wVersionRequired is a parameter provided to call this function, it tells the winsock interface wich version we want to use. We will use version 1.1. This info is passed to this function using the MAKEWORD macro. We don't care here about the WSADATA structure which is filled by the function. Usage: WSADATA wsa; WSAStartup(MAKEWORD(1,1),&wsa); To terminate the use of the winsock interface, WSACleanup() is used. Prototype: int PASCAL FAR WSACleanup(void); Usage: WSACleanup(); 2) socket(),closesocket(),connect() socket() is used to create a communication interface. prototype: SOCKET PASCAL FAR socket(int af,int type,int protocol); A handle is returned. Usage: SOCKET s; s=socket(AF_INET,SOCK_STREAM,0); We use SOCK_STREAM because we are interested in TCP/IP connections. closesocket() is used to close a communication interface. Usage: SOCKET s; closesocket(s); Therefore, if a connection to a remote socket exists it would be terminated. connect() is used to create a connection between two sockets, one of them is on a remote computer. In order to connect to a remote socket, using TCP/IP, we need an IP (internet address) and a port. prototype: int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen); To create a connection a socket is needed. namelen is the size of the structure passed as parameter here is the structure passed: struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; We are only interested in the fields sin_family, AF_INET for us and sin_port the port used to etablish the connection, and in the case of a client, we need to set a value in a field of the in_addr structure used by sockaddr_in. usage: #define PORT 23 SOCKET s; SOCKADDR_IN sin; const char ip="127.0.0.1"; sin.sin_family=AF_INET; sin.sin_addr.s_addr=inet_addr(ip); sin.sin_port=htons(PORT); connect(s,(LPSOCKADDR)&sin,sizeof(sin)); htons() is a function, translating usual decimal value used for the port into a value understandable by the winsock API. inet_addr() does the same work but for an IP. bind() is used mainly in the case of a server. A server has to wait for clients on a predefined port (example: 23 is the telnet default port). bind() is used to attach a local address (mainly a port) to a socket. Therefore, every request received by your computer will be transmitted to this socket. You cannot bind several sockets to the same port, it would be stupid to do that: how Windows would know wich sockets has to receive requests on a given port? prototype: int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen); paramaters are like those of connect(). Usage: #define PORT 23 SOCKET s; SOCKADDR_IN sin; sin.sin_family=AF_INET; sin.sin_addr.s_addr=0; sin.sin_port=htons(PORT); bind(s,(LPSOCKADDR)&sin,sizeof(SOCKADDR_IN)); Here we don't need an IP to pass, sin_addr.s_addr has to be 0. 3) listen(),accept() listen() is used by a server to set a socket in the state of waiting for client connection requests. prototype: int PASCAL FAR listen(SOCKET s,int backlog); backlog is the number of pending connections. When the socket will be busy be- cause a request connection is incoming, a certain number of clients can be wai- ting. If you set this value to 0, two incoming connections arriving at same time cannot be processed, one will receive an error message. This value hasn't to be confused with the number of clients handled by a ser- ver. Usage: SOCKET s; listen(s,0); If a Server is visited by few clients, it's unlikely two requests for connec- tions will arrive at same time. accept() is used when an incoming connection is arrived on the listening socket. prototype: SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR *addr,int FAR *addrlen); addr and addrlen fields are used to get info on the remote client you have just accepted, IP for example, but you can skip these fields. The returned value is a new socket handle that will be used to serve the client. When accept() function has been performed the socket used return to the liste- ning state, ready to accept new incoming connection requests. usage: SOCKET s1,s2; s2=accept(s1,0,0); 4) recv(),send() recv() is used to receive data from another (remote) socket prototype: int PASCAL FAR recv(int s,char FAR * buf,int len,int flags); usage: #define MAX_BUFFER 100; /*you cannot receive more than 4096 bytes at once*/ SOCKET s; char buffer[MAX_BUFFER]; int number_bytes_recv; number_byte_recv=recv(s,buffer,MAX_BUFFER,0); send() is used to send data to another (remote) socket prototype: int PASCAL FAR send(int s,char FAR * buf,int len,int flags); usage: #define MAX_BUFFER 100; /*you cannot send more than 4096 bytes at once*/ SOCKET s; char buffer[MAX_BUFFER]; int number_bytes_send; number_byte_send=send(s,buffer,MAX_BUFFER,0); 5) WSAAsyncSelect() This function is a Windows specific function. When writing TCP/IP communication we are facing some problems. The functions connect(),send() recv() are blocking by default. These functions will not re- turn. For example read() will not return until data are received. The program is freezing until some data are available on reading socket, we cannot accept this. Two ways are available to solve this problem, one is the UNIX way, using function select(), this article will not describe it. The other way, available to Win32 coders, is the use of WSAAsyncSelect(). WSAAsyncSelect() makes easier our work. Prototype: int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,\ long lEvent); hwnd is a handle for a window. wMsg is a user defined message. lEvent is a or'ing of events we want to monitor. To explain how it is used, here is an example: Imagine your client program wants to connect to a remote server. You apply first WSAAsyncSelect() to the socket used to connect and you perform a connect(). Since you have used WSAAsyncSelect() your connect() will not block whatever is happening but a message (wMsg) will be sent by Windows to your win- dow (hwnd) to notice your application the connection has been established suc- cessfully. WSAAsyncSelect() can be used to notify an application, data is available to be read on the socket (used to receive data). To do that, you specify you want to monitor the FD_READ event. When data will be available to be read, the message (wMsg) will be sent by Windows to your application (to the window object hwnd) and you have to call recv() in return to read. For a server, if you want to be alerted when a connection is requested, you have to specify FD_ACCEPT event when you apply WSAAsyncSelect() to the lis- tening socket. When your window will receive the message wMsg you have to call in return accept(). If you specify FD_CLOSE you will be notified when the remote socket has been closed, then call closesocket() in return. You can monitor several events at once, using "OR" but you use only one message (you specify) to be alerted. To determine which event was occuring you have to read the lParam associated to this message (in the window procedure). This va- lue will contain the event occuring. usage: 1) #define WM_MY_MESSAGE WM_USER /* to be sure you will not use a reserved value SOCKET s; SOCKADDR_IN sin; /*to monitor when the connection will be achieved: */ WSAAsyncSelect(s,WM_MY_MESSAGE,FD_CONNECT); (...) connect(s,(LPSOCKADDR)&sin,sizeof(sin)); 2) /*to monitor when data are available to be read: */ WSAAsyncSelect(s,WM_MY_MESSAGE,FD_READ); in the window procedure: case WM_MY_MESSAGE: recv(s,buffer,size); break; 3) /*to monitor when incoming connections are arriving: */ WSAAsyncSelect(s,WM_MY_MESSAGE,FD_ACCEPT); listen(s,0); /*socket listening for incoming connections*/ in the window procedure: case WM_MY_MESSAGE: s2=accept(s,0,0); /*accept the connection*/ break 4) /*to monitor if data are available to read OR if a disconnection has occured:*/ WSAAsyncSelect(s,WM_MY_MESSAGE,FD_READ | FD_CLOSE); in the window procedure: case WM_MY_MESSAGE: if(lParam==FD_READ) read(s,buffer,size); /*data are available to read*/ else closesocket(s); /*event FD_CLOSE has occured*/ break; VI) Examples of code source /********* Example 1: Example of a simple TCP/IP client.*/ #include #include #define MAXBUF 512 #define PORT 23 #define WM_SOCKET (WM_USER+100) LRESULT CALLBACK WndProcedure(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam); WNDCLASSEX wc={sizeof(WNDCLASSEX),0,WndProcedure,0,0,0,0,0,0,0,"vclass",0}; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine\ ,int nCmdShow) { MSG Msg; wc.hInstance=hInstance; RegisterClassEx(&wc); if(CreateWindowEx(0,"vclass",0,0,0,0,0,0,0,0,hInstance,0)) { while(GetMessage(&Msg,NULL,0,0)) { DispatchMessage(&Msg); } } return 0; } LRESULT CALLBACK WndProcedure(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam) { char buff[MAXBUF]; static SOCKET s; static WSADATA wsa; static SOCKADDR_IN sin; static char IP[]="127.0.0.1"; /*localhost IP*/ switch(Msg) { case WM_SOCKET: switch(lParam) { case FD_READ: recv(s,buff,MAXBUF,0); send(s,"ack",3,0); Beep(10000,1); /*if data have been received BEEP!*/ break; case FD_CLOSE: default: closesocket(s); break; case FD_CONNECT: send(s,"hi",2,0); break; } break; case WM_CREATE: /*message received when the window is created*/ WSAStartup(MAKEWORD(1,1),&wsa); s=socket(AF_INET,SOCK_STREAM,0); sin.sin_family=AF_INET; sin.sin_port=htons(PORT); sin.sin_addr.s_addr=inet_addr(IP); WSAAsyncSelect(s,hWnd,WM_SOCKET,\ FD_READ | FD_CONNECT | FD_CLOSE); connect(s,(LPSOCKADDR)&sin,sizeof(sin)); break; case WM_DESTROY: closesocket(s); WSACleanup(); MessageBox(0,"BYE!","CLIENT",0); PostQuitMessage(0); break; default: return DefWindowProc(hWnd,Msg,wParam,lParam); } return 0; } /********* End of the source code*/ /********* Example 2: Example of a simple TCP/IP server.*/ #include #include #define MAXBUF 512 #define PORT 23 #define WM_SOCKET1 (WM_USER+100) #define WM_SOCKET2 (WM_USER+101) LRESULT CALLBACK WndProcedure(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam); WNDCLASSEX wc={sizeof(WNDCLASSEX),0,WndProcedure,0,0,0,0,0,0,0,"vclass",0}; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine\ ,int nCmdShow) { MSG Msg; wc.hInstance=hInstance; RegisterClassEx(&wc); if(CreateWindowEx(0,"vclass",0,0,0,0,0,0,0,0,hInstance,0)) { while(GetMessage(&Msg,NULL,0,0)) { DispatchMessage(&Msg); } } return 0; } LRESULT CALLBACK WndProcedure(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam) { char buff[MAXBUF]; static SOCKET s,s2; static WSADATA wsa; static SOCKADDR_IN sin; switch(Msg) { case WM_SOCKET1: /*server is busy, no more clients accepted: */ if(s2) closesocket(accept(s,0,0)); /*connection accepted:*/ else s2=accept(s,0,0); WSAAsyncSelect(s2,hWnd,WM_SOCKET2,FD_CLOSE | FD_READ); break; case WM_DESTROY: if(s2) closesocket(s2); WSACleanup(); MessageBox(0,"BYE!","SERVER",0); PostQuitMessage(0); break; case WM_SOCKET2: if(lParam==FD_READ) { recv(s2,buff,MAXBUF,0); send(s2,"OK",2,0); } else { closesocket(s2); /*event FD_CLOSE has occured*/ s2=0; } break; case WM_CREATE: /*message received when the window is created*/ WSAStartup(MAKEWORD(1,1),&wsa); s=socket(AF_INET,SOCK_STREAM,0); sin.sin_family=AF_INET; sin.sin_addr.s_addr=0; sin.sin_port=htons(PORT); bind(s,(LPSOCKADDR)&sin,sizeof(SOCKADDR_IN)); listen(s,0); WSAAsyncSelect(s,hWnd,WM_SOCKET1,FD_ACCEPT); break; default: return DefWindowProc(hWnd,Msg,wParam,lParam); } return 0; } /********* End of the source code*/ /********* Example 3: Example of a simple TCP/IP server, capable to serve several clients at once.*/ #include #include #define MAXCLIENT 2 #define MAXBUF 512 #define PORT 23 #define WM_SOCKET1 (WM_USER+100) #define WM_SOCKET2 (WM_USER+101) LRESULT CALLBACK WndProcedure(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam); WNDCLASSEX wc={sizeof(WNDCLASSEX),0,WndProcedure,0,0,0,0,0,0,0,"vclass",0}; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine\ ,int nCmdShow) { MSG Msg; wc.hInstance=hInstance; RegisterClassEx(&wc); if(CreateWindowEx(0,"vclass",0,0,0,0,0,0,0,0,hInstance,0)) { while(GetMessage(&Msg,NULL,0,0)) { DispatchMessage(&Msg); } } return 0; } LRESULT CALLBACK WndProcedure(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam) { char buff[MAXBUF]; int i; unsigned int testmsg; static SOCKET s; static SOCKET s_client[MAXCLIENT]; static WSADATA wsa; static SOCKADDR_IN sin; testmsg=Msg-WM_SOCKET2; if(testmsg