Beginning Winsock Programming - Simple TCP client

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().

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.

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.

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 :-

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.

Share

About the Author

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.

Comments and Discussions

The section "Getting host information" can be improved by eliminating the call of gethostbyaddr as in order to connect we need the address (addr) only and not the whole hostent info. Moreover call to gethostbyaddr issues NBNS queries, so it doesn't work if the remote machine doesn't support NetBIOS Name Services (e.g. IBM mainframes). Duplicated call of inet_addr can also be reduced.So the suggested code is:

Thanks for the code. Unfortunately, it doesn't function in a very stable manner. The file linked to in the project no longer exists. A crash occurs on trying to download it. After changing the url to "http://s.codeproject.com/App_Themes/Std/Img/logo225x90.gif" (CP bob on top left of each page), the program didn't crash - however it only downloads and saves about 3 kb of the image (img is about 6kb)

If you came to this page because you are searching a professional TCP socket class with these features:

- easy to use
- clean and well commented code
- non-blocking (asynchronous) and event driven.
- functionality for Client and Server
- up to 62 clients can be connected at the same time
- support of multiple network adapters
- thread safe
- a class which can even run in the one and only GUI thread
- event handling optimized for servers under stress

the problem is as bellow:
first, the Server accept the client connection, and communicate with the client, everything is ok.
but, after the server close, the operation send in the client still successful, none error. After sending, i try to recv, for now, i get the error(10054).

My program connects to the host computer using its Hostname & not its IP address. (this is a requirement)

On running my program on my local computer:

1. I can successfully connect to MyHost using HTTP over TCP/IP on Port 80.
2. I can successfully send [send()] data to MyHost.
3. I can successfully receive [recv()] the response from MyHost.

All communication is absolutely fine & works well.

Now here's problem:

If now - I turn off DHCP on MyHost & provide it a static IP address, say 157.184.205.111 - my program cannot connect to MyHost anymore.

I have also observed the following:

1. If, from within my program, I try to resolve the IP address of MyHost using the gethostbyname() function & other calculations, I receive the same old IP address of MyHost (157.184.205.117) instead of the newly assigned static IP address (157.184.205.111).

2. If I PING to MyHost from the Windows XP Command Prompt, it tries to connect to the old IP address of MyHost (157.184.205.117) instead of the newly assigned static IP address (157.184.205.111). So pinging also fails.

3. If I try to open in Internet Explorer, the url "http://MyHost" it fails to open the address.