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.