
Introduction
This is a sequel to the article Beginning Winsock Programming - Simple TCP server
and if you have not read that already I would recommend that you do that first.
In this article I'll show how you can write a simple TCP client program. We'll
write a program that will connect to an HTTP server and retrieve a file.
Program Flow of a simple TCP client
- Initialize WinSock library using
WSAStartup()
- Create a IPPROTO_TCP SOCKET using
socket()
- Retrieve host information using
gethostbyname()/gethostbyaddr()
- Connect to the server using the socket we created, using
connect()
- Send and Receive data using
send()/recv() till our tcp chat is over
- Close the socket connection using
closesocket()
- De-Initialize WinSock using
WSACleanup()
Initialize WinSock
As with every other WinSock program we need to initialize the WinSock
library. Basically it is also a kind of check to see if WinSock is available on
the system in the precise version we expect it to be.
int wsaret=WSAStartup(0x101,&wsaData);
if(wsaret)
return;
Create the SOCKET
The socket is the entity that acts as the endpoint between the client and the
server. When a client is connected to a server, there are two sockets. The
socket at the client side and the corresponding socket at the server side. Lets
call them CLIENTSOCK and SERVERSOCK. When the client uses send() on CLIENTSOCK
the server can use recv() on the SERVERSOCK to receive what the client sends.
Similarly the reverse is also true. For our purposes we create the socket using a
function called socket().
SOCKET conn;
conn=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(conn==INVALID_SOCKET)
return;
Getting host information
Obviously we need to get info about the host [the server] before we can
connect to it. There are two functions we can use - gethostbyname() and gethostbyaddr(). The
gethostbyname() function is used when we have the DNS name
of our server, something like codeproject.com or ftp.myserver.org. The
gethostbyaddr() function is used when we actually have the IP address of the
server to connect to, something like 192.168.1.1 or 202.54.1.100.
Obviously we would want to give our end user the option of entering either a
DNS name or an IP address. Thus for making that part of it transparent to him,
we do a little trick as shown below. We use the function inet_addr() on the
entered string. The inet_addr() function converts an IP address into a standard
network address format. Thus if it returns failure, we now know that the string
cannot be an IP address, if it succeeds we assume that it was a valid IP
address.
if(inet_addr(servername)==INADDR_NONE)
{
hp=gethostbyname(servername);
}
else
{
addr=inet_addr(servername);
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}
if(hp==NULL)
{
closesocket(conn);
return;
}
Connecting to the server
The connect() function is used to establish a connection to the destination
server. We pass it the socket we created earlier as well as a sockaddr
structure. We populate the sockaddr with the host address returned by gethostbyname()/gethostbyaddr(), as well as enter a valid port to connect to.
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons(80);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
{
closesocket(conn);
return;
}
Chatting
Once the socket connection is established the client and the server can
send() and recv() data between themselves. This is popularly referred to as TCP
chatting. In our particular case we need to HTTP chat, which is comparatively
simple when you consider other slightly more complicated protocols like SMTP or
POP3. The HTTP GET command is used to retrieve a file from the HTTP server. This
might be an HTML file or an image file or a zip or an MP3 or whatever. It is
send thus [in it's simplest form]. There are other slightly more complex ways of
using this command.
GET http-path-to-file\r\n\r\n
And in our program we do something like this to send the GET command :-
sprintf(buff,"GET %s\r\n\r\n",filepath);
send(conn,buff,strlen(buff),0);
Once we have send the command we know that the server is going to start
sending us the file we just requested. Just as we used send() to send our
command we can use recv() to receive the data that the server is going to send
us. We loop on recv() till it returns zero when we understand that the server
has finished sending us the data. And in our particular case we write all this
data to a file as our intention is to download and save a file.
while(y=recv(conn,buff,512,0))
{
f.Write(buff,y);
}
Close the connection
Now that our chat is over, we must close the connection. In our case the HTTP
connection is closed by the server the moment it finishes sending the file, but
that doesn't matter. We need to close our socket and release the resource. In
more complex chats we usually call shutdown() before we call closesocket() to
ensure that the buffers are flushed. Otherwise we might encounter some data
loss.
closesocket(conn);
De-Initialize WinSock
We call WSACleanup() to conclude our usage of WinSock.
WSACleanup();
Thank you.
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site -
www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff -
blog.voidnish.com.
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy
Summer Love and Some more Cricket as well as a programming book –
Extending MFC applications with the .NET Framework.
Nish's latest book
C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.