Now that you've seen how a blocking c lient works, it's time for the blocking server example. This c hapter will explain how to build a simple server that ROT13 enc odes the received data and then sends it bac k. ROT13 (rot stands for rotate) is a very simple encryption method used by Caesar.
Each c harac ter in the alphabet is replaced by the charac ter 13 positions farther (the characters rotate 13 places). The encryption is symmetric, that is enc ryption works exac tly the same as dec rypting. You c an use rot13.com if you want to play with it.
1. Program flow
The program flow is as follows:
The server creates a server socket
The server socket is bound to an address The server socket is put into the listening state
On c onnection attempt, the c onnec tion is ac c epted and a c lient socket is available The client socket is read, every byte is ROT13'd and sent back.
When the client c loses the c onnection, the program ends 2. Frame work
The framework is almost the same as that of the blocking c lient example from chapter 6, only the HRException has been renamed to ROTException and some include files were added:
#include <iostream>
#include <string>
#include <sstream>
#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>
using namespace std;
class ROTException {
public:
ROTException() :
m_pMessage("") {}
virtual ~ROTException() {}
ROTException(const char *pMessage) : m_pMessage(pMessage) {}
const char * what() { return m_pMessage; } private:
const char *m_pMessage;
};
int main(int argc, char* argv[]) {
// main program }
3. Constants and global data
The program uses a few c onstants for the default server port number (4444), the required winsoc k version and rec eive buffer size.
const int REQ_WINSOCK_VER = 2; // Minimum winsock version required const int DEFAULT_PORT = 4444;
4. T he main function
The main function too has a lot of c ommon with the blocking client from the previous c hapter:
int main(int argc, char* argv[]) {
int iRet = 1;
WSADATA wsaData;
cout << "Initializing winsock... ";
if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER,0), &wsaData)==0) {
// Check if major version is at least REQ_WINSOCK_VER if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER) { final port number as its parameter. The RunServer function c ontains the ac tual server c ode.
5. RunSe rv e r
if ((hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) throw ROTException("could not create socket.");
cout << "created.\n";
// code goes here
}
This function is c alled in RunServer in the following way:
// Bind socket
cout << "Binding socket... ";
SetServerSockAddr(&sockAddr, portNumber);
if (bind(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr))!=0) throw ROTException("could not bind socket.");
cout << "bound.\n";
7. Le tting the socke t liste n
If the binding suc c eeds, the socket is put into listening mode. As soon as it's in this state, any c lient c an make a c onnection attempt to the server on the port the soc ket is bound to. Setting the listening mode is simply done by c alling the listen winsock function:
// Put socket in listening mode
cout << "Putting socket in listening mode... ";
if (listen(hSocket, SOMAXCONN)!=0)
throw ROTException("could not put socket in listening mode.");
cout << "done.\n";
Listen has two parameters. The first is the socket you want to listen, the second is the length of the queue of pending connec tions. Usually the default value of SOMAXCONN is okay for the latter parameter. This value is the maximum number of c onnections that winsock will hold pending until Blocking sockets: server - MadWizard.org
your program ac cepts them. You probably don't need to worry about this value most of the time.
8. Acce pting conne ctions
When the server socket is in the listening state, you need to acc ept the incoming c onnec tions using the accept function. The ac c ept function bloc ks until a c onnec tion request c omes in, establishes the connec tion and then returns a c lient socket handle. It is important to know that the server socket's only purpose is now to listen for c onnec tions and acc ept them. As soon as you acc ept a connection, a new socket is c reated by winsock. This socket is usually c alled the client socket and that's the socket you will be rec eiving and sending data on. This often confuses winsock beginners, some try to rec eive or send data on the listening soc ket, while they should use the c lient socket.
Besides ac c epting a c onnec tion and returning a c lient socket handle, accept also fills in a soc kaddr_in structure with information about the client. Our example will use this information to print a short desc ription of the c lient that connec ted (in the form IP:port).
sockaddr_in clientSockAddr;
int clientSockSize = sizeof(clientSockAddr);
// Accept connection:
throw ROTException("accept function failed.");
cout << "accepted.\n";
// Wait for and accept a connection:
HandleConnection(hClientSocket, clientSockAddr);
The above c ode c alls acc ept, and then handles the both the client socket handle and the soc kaddr_in structure to a new function, HandleConnec tion, which will deal with the c onnec tion.
After this c ode has exec uted, RunServer returns and c loses the sockets, as shown earlier.
9. Handle Conne ction
// Print description (IP:port) of connected client
cout << "Connected with " << GetHostDescription(sockAddr) << ".\n";
char tempBuffer[TEMP_BUFFER_SIZE];
// todo
cout << "Connection closed.\n";
}
The GetHostDesc ription func tion looks like this:
string GetHostDescription(const sockaddr_in &sockAddr) {
ostringstream stream;
stream << inet_ntoa(sockAddr.sin_addr) << ":" << ntohs(sockAddr.sin_port);
return stream.str();
}
We will now write the part marked as 'todo' in the above HandleConnection framework. The function is called to enc rypt the rec eived data and finally send is used to send the encrypted data bac k to the c lient:
// Read data while(true) {
int retval;
retval = recv(hClientSocket, tempBuffer, sizeof(tempBuffer), 0);
if (retval==0)
throw ROTException("socket error while receiving.");
}
if (send(hClientSocket, tempBuffer, retval, 0)==SOCKET_ERROR) throw ROTException("socket error while sending.");
protocol to Raw. Finally press Open to c onnec t.
You will now see putty's console window. Here you c an type text that will be send to the server.
Any data rec eived will be printed in the same window. Note: putty by default has local line editing enabled. This means that you c an type and even edit the text you type as long as you stay on the same line, sinc e it's not send until you press enter. If you use a c lient that immediately sends every c harac ter, you also get a response immediately. If you have suc h a c lient you should disable local ec ho (ie. showing the text you type), otherwise you get your text and the received text interleaved, whic h is pretty hard to read. This is not the c ase with putty. Here's a sc reenshot of the c onnec tion in action:
11. Source code
Finally, the sourc e c ode:
Download the source zip file here:
12. Conclusion
Now you've seen both a blocking client and a blocking server. Blocking soc kets are relatively easy to use bec ause they fit in nic ely in the program flow. Still, you've only seen pretty simple examples, since both the c lient and the server we showed did prac tic ally nothing with the data other than print it or in this c ase, encrypt and then send it bac k. It gets harder when we have to extrac t meaningful information from the rec eived data like when dealing with a protocol like POP3.
© 2010 by Thomas Bleeker (MadWizard)
http://www.madwizard.org/download/winsock/rot13server_cpp.zip