Introduction
I needed to write an asynchronous TCP interface and quickly discovered the windows class CAsyncSocket
. However, much searching and reading failed to find a complete description of how it works and how to use it. Much time and many questions flowed under the bridge in the quest to learn the basics. The result is an application that makes it simple for me to see how this class works.
In this Part One, I describe how class CAsyncSocket
works and how to use it in a simple application. Part 2 presents the core code that implements the classes. It is found here.
For as long as this article is, and as difficult as it was to find the information I needed, I was quite surprised upon realizing how simple the basic concept is. The implementation is much easier than expected.
All Four Articles
Late Addition. This is a short summary of all four articles and a link to each.
I have completed four articles on asynchronous TCP/IP and Microsoft's class ASyncSocket
. Part 1 describes the necessary concepts. Part 2 describes a single project that incorporates a server and a client in a single project and a single dialog. The user can walk through a transaction one step at a time. In parts 3 and 4, the Server and Client are separated into two projects residing in one solution. The Server and Client can run on separate computers. That project introduces the concept of multiple projects in a single solution. It introduces the concept of using source code from a separate directory. If you are not well versed in TCP/IP and with ASyncSocket
, the first two articles are a must read. If you have not worked with multiple projects in one solution, or with Additional Include Directories, Part 3 is a must read. If that sentence looks weird, please read Part 3.
Listed below are links to each article:
Prerequisites
There are many descriptions of TCP/IP operations. This article presumes the reader understands the basics of TCP/IP operations. The reader is also presumed to be able to create an MFC project, populate it with buttons and other controls, make those controls do things, and display the results.
Caution
This is a demonstration application for CAsyncSocket
. The addition of all the code really needed to make it flexible and to handle any foreseeable problem will obscure the fundamentals. It is not bullet proof. The reader can probably find a way to crash it. This is a demonstration tool, not a working application.
Why Asynchronous?
There are many applications that can work while paying attention to primarily one thing at a time. There are many more that must pay attention to multiple things simultaneously. There are three basic types of operations.
- Synchronous: An application initiates something and waits for the result. Essentially, all function or methods calls are synchronous.
- Polled: An application starts something, continues work on something else, then at a later time, returns to check on the result. Polling consumes resources that may be needed elsewhere.
- Asynchronous: An operation initiates an operation, then does something else while waiting for an event to trigger something else to happen, that is asynchronous operations.
Asynchronous can be sub-divided into two or more groups. Under the DEC (Digital Equipment Corporation) operating system VMS, the user would initiate an operation such as I/O operation. In the calling argument would be the address of a function to run when the I/O completes. When the I/O operation completes, the OS (Operating System) would call that function at a slightly higher priority than the main task. That is a call back operation. It is event driven.
In Windows, when the operation completes, the OS posts a message to the application. Some time later when the application runs again, the top levels of the application checks the message queue, finds that message, then calls a method. This is also event driven, but instead of calling the function directly, the OS sets a flag to have it called. These called methods are the On*()
methods you will see shortly. (Note: The wild card * is used in the phrase On*()
to refer to multiple methods that all begin with On
.
From the perspective of this application, and probably most applications that are like it, the difference is not visible to the user. But they are different.
The Test Application
After reading many descriptions of how to use CAsynchSockets
, I was still at a loss as to how to use it. The result is the MFC dialog application presented in these articles. This application does very little, but it was the vehicle I used to assemble everything together, get them playing well with each other, and see how they work. Here is a screen shot taken after one message has been sent from the Server to the Client.
It is a rather busy application, but asynchronous sockets are far from trivial.
There are three groups of controls, each of which has several columns. On the left, with four sub-columns, is the Server. On the right, the last four columns are the Client. In the middle is C_Server_Send
with three columns. Below each of the groups is the status section.
Server and Client sections are each divided into two sections:
- The button to initiate the action and a status field showing the result of that button click.
- A column of
On*()
counts and their counted values. For example: The label OnAccept()
and to its right a count display, currently set to 1 (one). We will get to this until later.
In the C_Server_Send
group, the dialog was getting crowded so I omitted the status column. At the bottom of each section is a display of the WSA error code. Simply put, this is the Windows status for each TCP operation. Google it for more information. Look up that 10035 value and save a link to your favorite web page that lists them all.
At the bottom is Received Time. After startup, the user clicks button 6: Send and the server sends time to the client. Then upon pressing button 7: Receive, the client captures the data and displays it. In this case, the received time shown in HH:MM:SS.xxx. That is all this application does.
Note the numbers in the buttons, 1 through 7. I sometimes forgot where I was in the process so the buttons are labeled with the order of operations. To initialize the connection between the server and client, select buttons 1 through 5 in order. To update the time, repeat buttons 6 and 7. The 6: causes the server to send out time, the 7: causes the client to read it. In this learning application, very little takes place automatically. Each major step is activated with its own button. In operational code, that must change.
The reader will eventually observe there are unused buttons and unused display values. During the creation of this application, and from reading various articles, I read about the various On*()
methods and their overrides. Not understanding their meaning, I provided each one with a skeleton method and code to count the number of times it is called.
This is my first attempt at Windows asynchronous operations and I have decided to capture the minimal functional level. The server is simple and does nothing but send time. The client simplicity matches the server and does nothing but receive and display time. If you want more, you can add it.
The reader should note that this application is a complete TCP/IP application containing both the Server and the Client. All in one, it talks to itself. I believe this will operate from two computers if the user adjusts the IP addresses and runs a copy on each computer. But that is for later. I found it simpler to do everything in one application.
CAsyncSocket Basics
In this section, essentially all details of the code are omitted and the basic operations are described. Rest assured that the key sections of code are as simple as this description. The reader is encouraged to reference the figures while reading this description. Keep in mind, each major operation in the code is activated by a separate button click. To send and received the first message, only seven clicks are required.
There are three classes in this application: C_Server
, C_Client
, and C_Server_Send_Time_Socket
. All three of these derived from the Windows class CAsyncSocket
. Every reference made to the base class is a reference to CAsyncSocket
from which each class is derived. Each of these classes is customized for its purpose, but all use the same base class.
In all TCP/IP operations there is a Server and a Client. Generally, the server has resources that the client wants. In this demonstration application, the server has the time and sends it to the client. Nothing more.
Server Initialize
The server side of the application instructs the class C_Server
to perform its various operations.
Begin with server button 1: Initialize. The C_Server
class tells the base class to initialize. The code really is that simple. The base class CAsyncSocket
handles all the details.
Server Listen
Button 2: Listen, puts C_Server
into a listen mode. The base class makes the necessary Windows calls and Windows takes all the action necessary to create a TCP port and begin waiting for a client to connect. This is where a blocking application would stop. All other activities would cease until the Listen operation received a response. With async operations, the application can attend to other tasks, such as communicating with other clients.
This is the point where I rely on the reader to understand what has happened so far. It you do not understand these events, find and read some TCP/IP documentation, then return to this article.
Client Initialize
Now we activate class C_Client
. In the client section button three, 3:Initialize, does the same for the client side of things.
Client Connect
Select button 4: Connect, to send out a connect request to the server. The code is almost that simple. Windows handles all the details to build and send out a TCP message that finds the Server and says: There is a Client that wants to connect to your server application.
Server Accept
This is the point where things become a bit difficult. That is a bit of a miss-statement. This is where it becomes difficult to find a good explanation.
I explain by digressing for a while to discuss standard dialog operations. The creation of button, call it Do_A
, eventually results in a method named something like: OnButtonClickedDo_A()
. When this method is called, it means that button Do_A
has been pressed. This function is to contain the code that is to do something.
In this Server
class, method OnAccept()
does not mean that the request from the client has been accepted, it means that the application has recognized a request from the client and the programmer must now do something to accept that request. It means you are now ready to perform the OnAccept
operation. As I read about this class, the fundamental meaning of an OnSomething()
method shifts from Something_Happened
, to Now_You_Can_Do_Something
. The user must understand the purpose of each method. Hopefully, this is explained below. Now we return to this application discussion.
In this minimal application, the only thing to do is send time from Server
to Client
. In your real world application, there will probably be much to do. We will keep it simple for now.
The OnAccept()
method does nothing more than increment the counter for OnAccept()
and update the display. Here is a screen shot of the application immediately after the Client
button four, 4:Connect, has been pressed. Button 5 has NOT been clicked, only the first four buttons have been clicked.
Note that server buttons 1 and 2 have OK next to them and Clients 3 and 4 have OK next to them. In the Server section, field OnAccept()
has changed from 0 to 1 (zero to one). It has been called. The server Accept button still has its status field set to the initial value Status. That button has not been selected yet. Note also that in the Client section, the counter fields for OnConnect()
and OnSend()
both now display 1 (one). This simple application ignores both. However: for more advanced applications, you will need to know that these functions have or have not been called, and when. Eventually, you probably need to learn about them, but not now.
It is interesting that things have happened on the Client side, but the Server side has yet to complete its duties. You can ignore the WSA error code for now, or look it up and discover the meaning. (Google on wsa error 10035) Now for button five.
About the On*() Methods
Before presenting button five, the On*()
methods merit a brief functional explanation, but the details are deferred to part 2. The windows class ASyncSocket
must be used as the base to a class you create. In that class is a group of methods that must have over-rides. This list includes:
OnAccept()
OnClose()
OnConnect
OnOutOfBandData()
OnReceive()
OnSend()
There may be others that I am unaware of. In a real application, you will write code in some or all of these methods to accomplish various tasks. In this demonstration application, the code in these methods just counts the number of times the method is called and displays it. If you use this application to write some more test code, and when you solicit a call to these methods, the counter will show that has happened. Adding these display fields and the code to drive them does add a fair amount of clutter to the application, but now the application shows which On*()
is called and when. Commercial break is over and we return to the scheduled broadcast.
Server Accept
Now that we see the OnAccept()
count set to 1
, we know the time has arrived to accept the Client request. There is some serious activity here.
More preparatory explanation is required.
A server application is seldom created to serve just one client. It almost always has the capability to serve multiple clients simultaneously. In this application, there can be only one client, but we still must go through the necessary motions as through there might be many.
At this point in the discussion, class C_Server
has performed the task of listening for the client request. The next task is to prepare to communicate with the specific client. The third class, C_Server_Send_Time_Socket
, is used to conduct the continuing conversation between the server and the client. This class is implemented to send time to the client.
Back to the previous screen shot, the user has clicked button 4 and the OnAccept()
function has been called. The counter shows us this activity. That method’s purpose and meaning is:
A Client has initiated a connection with the Server. Now is the time to do something about that connection.
The next step is button 5: Accept. (Don’t click it yet.) Pressing this button calls the C_Server Accept()
method that has two essential operations:
- Create an instance of
C_Server_Send_Time_Socket
- Associate that instance with the new Client connection.
Each Server can communicate with multiple clients. For each client, there must be one instance of class C_Server_Send_Time_Socket
. Button 5:Accept performs these two operations. Be reminded that this limited demonstration application can connect to only one Client.
First, a caution. In real code, you will almost certainly program the OnAccept()
method to perform the operations that are performed here with button 5:Accept. This demonstration application provides the ability to step through each major operation one step at a time.
Our button 5:Accept (keep reading before clicking it) first creates a new instance of class C_Server_Send_Time_Socket
. (One line of code.) Then it goes to the base class method Accept()
, and calls it. The argument to that call is the just created instance of class C_Server_Send_Time_Socket
. Within the confines of the base class, Windows associates that object with the client that just sent the Connect signal. That makes two lines of code to form the core of the accept()
method.
As a result of the Accept()
call, the new instance of C_Server_Send_Time_Socket
is connected to the client and is ready to send data to the client. Meanwhile, if your server will connect to multiple clients, you will be adding code to recycle C_Server
and get ready for the next client. In this demo application, we now leave C_Server
alone and switch to C_Server_Send_Time_Socket
.
Prepare to Send Data to Client
Now click 5:Accept. Not much appears on the screen.
The only thing different is that under C_Server_Send
the count for OnSend()
has incremented to 1 (one).
Again, unlike the OnButtonClicked*()
methods we are accustomed to, this call to OnSend()
does not mean that something has been sent. It means that this instance of C_Server_Send_Time_Socket
is now ready to send data.
Send Data To Client
In this demo application, the Client does not ask for anything, and the Server does not send anything automatically. I considered putting a timer in C_Server_Send
and update time every so often. Instead I went for simple. Each time 6:Send is clicked, the time is captured from Windows and sent to the client.
Now click 6:Send.
The results might appear a bit underwhelming, but this is the amazing culmination of the Server side. It has sent the time to the Client, and the OnReceive()
counter for the client has incremented to 1 (one). This time when Windows arranges to have OnReceive()
called, it does not mean it is OK to receive data, it means that Windows has received some data from the client and now the client can fetch, or receive that data from Windows.
In your real application, you will write code in method OnReceive()
to capture that data and do something with it. In this demo application, we only observe that there is some data to receive. Notice the careful choice of words. The data sent by the Server is ready, but has not yet been received by the application.
BTW: The send
method has one line of code to get the time, one to get the size, and one more to send it. Three core lines of code are all that is needed.
Get Data From Server
Now click 7:Receive. The client class goes to its base class and calls Receive( buffer, size, flags)
. This is where the application completes the server to client data path. Now the Client has the data and can do whatever is needed with said data. (One line of core code.) When you do this, you will see the time update at the bottom of the dialog. The first screen capture in this article shows the application after a data send and receive.
Click buttons 6 and 7 to repeat the operation. Click 6 two times before clicking 7 and see how things work. Click 7:Receive without a 6:Send. Then draw your own conclusions about what is happening within Windows.
Before going on to part two, read through this article a couple of times and be certain you understand the basic concepts.