I. Summary
This paper describes the asynchronous implementation based on TCP communication protocol.
Second, the experimental platform
Visual Studio 2010
Principle and common methods of asynchronous communication
3.1 Establishing the connection
In synchronous mode, the Accept method is used to access the connection request on the server, and the Connect method is used to connect to the server at the client. In contrast, in asynchronous mode, the server can use the BeginAccept method and the Endaccept method to complete the task of connecting to the client, and the BeginConnect method and the EndConnect method are used to implement the connection to the server.
BeginAccept the incoming connection attempt in an asynchronous manner, which allows other actions to continue without waiting for the connection to be established. Before calling BeginAccept, you must use the Listen method to listen for connection requests, and the BeginAccept function prototype is:
BeginAccept (AsyncCallback AsyncCallback, Ojbect State)
Parameters:
AsyncCallback: Representing callback functions
State: status information must be guaranteed to contain a socket handle
The basic process for using beginaccept is:
(1) Create the local end node and bind the new socket to the local end node;
(2) Listening for new connection requests on the port;
(3) The request begins to access the new connection, passing in the instance of the socket or stateojbect.
Reference code:
Define IP address
ipaddress local = Ipaddress.parse ("127.0,0,1");
IPEndPoint IEP = new IPEndPoint (local,13000);
Create the socket object for the server
socket server = new socket (ADDRESSFAMILY.INTERNETWORK,SOCKETTYPE.STREAM,PROTOCOLTYPE.TCP);
Server. Bind (IEP);
Server. Listen (a);
Server. BEGINACCECPT (New AsyncCallback (Accept), server);
When the BeginAccept () method call is complete, the callback function is invoked once the new connection occurs, and the callback function must include the Endaccept () method to end the access connection operation.
The method argument list is Socket endaccept (IAsyncResult IAR)
The following is an instance of the callback function:
void Accept (IAsyncResult iar)
{
//restore incoming original socket socket
MyServer = (Socket) IAR. asyncstate;
Invokes the Endaccept method on the original socket, returning the new socket socket
service = myserver.endaccept (IAR);
}
So far, the server side is ready. The client should connect to the host remotely through the BeginConnect method and EndConnect. When calling the BeginConnect method, you must register the corresponding callback function and pass at least one instance of the socket to the state parameter to ensure that the original socket is available in the EndConnect method. The following is a BeginConnect call:
Socket socket=new Socket (addressfamily.internetwork,sockettype.stream,protocoltype.tcp)
IPAddress ip= Ipaddress.parse ("127.0.0.1");
IPEndPoint iep=new IPEndPoint (ip,13000);
Socket. BeginConnect (IEP, new AsyncCallback (Connect), socket);
EndConnect is a blocking method that completes the asynchronous connection of the BeginConnect method to the request of the remote host. The IASYNCCREUSLT returned by the BeginConnect method must be received as an argument after the callback function has been registered. The following code demonstrates:
void Connect (IAsyncResult iar)
{
socket client= (socket) IAR. asyncstate;
Try
{
client. EndConnect (IAR);
}
catch (Exception e)
{
Console.WriteLine (e.tostring ());
}
Finally
{
}
}
In addition to using the above method to establish a connection, you can also use the method in the TcpListener class to establish the connection. The following is a server-side attempt to process an incoming connection using the Beginaccetptcpclient method on the TcpListener class. Here's the code using the Beginaccetptcpclient method and the Endaccetptcpclient method:
public static void Dobeginaccept (TcpListener listner)
{
//start listening for connections from client
Console.WriteLine ("Waitting for a" Connection ");
Receive connection
//start ready to access new connection, call callback function Doaccepttcpcliet Listner once a new connection attempt is attempted
. Beginaccepttcpclient (New AsyncCallback (Doaccepttcpcliet), listner);
}
Handling client connections public
static void Doaccepttcpcliet (IAsyncResult iar)
{
//restore original Tcplistner object
TcpListener listener = (tcplistener) IAR. asyncstate;
Completes the action of the connection and returns the new TcpClient
tcpclient client = listener. Endaccepttcpclient (IAR);
Console.WriteLine ("Successful Connection");
}
The code's processing logic is:
(1) Invoke the Beginaccetptcpclient method to start connecting the new connection, and when the connection view occurs, the callback function is invoked to complete the connection operation;
(2) The above Doaccepttcpcliet method obtains the Listner instance which is passed by the beginaccepttcpclient through the AsyncState property;
(3) After getting the listener object, use it to call the Endaccepttcpclient method, which returns the new tcpclient containing the client information.
The BeginConnect method and the EndConnect method can be used by the client to try to establish a connection to the server, and there is no difference between this and the first method. Look at the example below:
public void Dobeginconnect (IAsyncResult iar)
{
socket client= (socket) IAR. asyncstate;
Start connecting to the remote host
client. BeginConnect (serverip[0],13000,requestcallback,client);
Console.WriteLine ("Start Connecting to Server");
}
private void Requestcallback (IAsyncResult iar)
{
try
{
//restore original TcpClient object
tcpclient client= (tcpclient) IAR. asyncstate;
client. EndConnect (IAR);
Console.WriteLine ("Connection to server {0} succeeded", client. client.remoteendpoint);
}
catch (Exception e)
{
Console.WriteLine (e.tostring ());
}
Finally
{
}
}
These are the two ways to establish a connection. You can choose to use it as needed.
3.2 Sending and receiving data
After a socket connection is established, data communication between the server and the client is possible. Asynchronous sockets are responsible for sending data using the BeginSend and Endsend methods. Note that before calling the BeginSend method, make sure that both sides have established a connection, otherwise an exception will occur. The following demo code:
private static void Send (Socket handler, string data)
{
//Convert The string data to byte using ASCII Encodi Ng.
byte[] Bytedata = Encoding.ASCII.GetBytes (data);
Begin sending the data to the remote device.
Handler. BeginSend (bytedata, 0, bytedata.length, 0, New AsyncCallback (Sendcallback), handler);
private static void Sendcallback (IAsyncResult ar)
{
try
{
//Retrieve the socket from the ' state object.
Socket handler = (socket) ar. asyncstate;
Complete sending the data to the remote device.
int bytessent = handler. Endsend (AR);
Console.WriteLine ("Sent {0} bytes to client.", bytessent);
Handler. Shutdown (Socketshutdown.both);
Handler. Close ();
}
catch (Exception e)
{
Console.WriteLine (e.tostring ());
}
}
Receive data is through the BeginReceive and EndReceive methods:
private static void Receive (Socket client) {try {//Create the state object.
StateObject state = new StateObject ();
State.worksocket = client;
Begin receiving the data from the remote device. Client.
BeginReceive (state.buffer, 0, stateobject.buffersize, 0, New AsyncCallback (ReceiveCallback), state);
catch (Exception e) {Console.WriteLine (e.tostring ());
} private static void ReceiveCallback (IAsyncResult ar) {try {//Retrieve the state object and the client socket
From the asynchronous state object. StateObject state = (stateobject) ar.
asyncstate;
Socket client = State.worksocket;
Read data from the remote device. int bytesread = client.
EndReceive (AR);
if (Bytesread > 0) {//There might is more data, so far of the data received so.
State.sb.Append (Encoding.ASCII.GetString (state.buffer, 0, bytesread));
Get the rest of the data. Client. BeginReceive (state.buffer, 0, stateobject.buffersize, 0, new AsyncCallback (ReceiveCallback), state);
else {//All the data has arrived, put it in response.
if (State.sb.Length > 1) {response = state.sb.ToString ();
}//Signal that all bytes have been received.
Receivedone.set ();
The catch (Exception e) {Console.WriteLine (e.tostring ());
}
}
The processing logic for the preceding code is:
(1) The communication socket client obtained from the callback function of the connection is processed first, then the data is received;
(2) When data is sent to the buffer, the BeginReceive method attempts to read a block of data of length buffer.length from the buffer array and returns the amount of data received bytesread. Finally receive and print the data.
In addition to the above methods, you can use asynchronous send and receive methods based on NetworkStream, and the following is an introduction to the use of NetworkStream-related asynchronous send and receive methods.
NetworkStream use the BeginRead and EndRead methods for read operations, write using the Beginwreite and Endwrete methods, and see examples below:
static void Datahandle (TcpClient client)
{
TcpClient tcpclient = client;
Use the TcpClient GetStream method to get the network stream
networkstream ns = Tcpclient.getstream ();
Check that the network stream is readable
if (NS). CanRead)
{
//definition buffer
byte[] read = new byte[1024];
Ns. BeginRead (Read,0,read. Length,new AsyncCallback (myReadCallBack), NS);
else
{
Console.WriteLine ("Cannot read stream data from Network");
}
public static void myReadCallBack (IAsyncResult iar)
{
NetworkStream ns = (networkstream) IAR. asyncstate;
byte[] Read = new byte[1024];
String data = "";
int recv;
recv = ns. EndRead (IAR);
data = String.Concat (data, Encoding.ASCII.GetString (read, 0, recv));
The message received may be longer than the total size of the buffer, looping until it is read until the while
(NS). dataavailable)
{
ns. BeginRead (read, 0, read. Length, New AsyncCallback (myReadCallBack), NS);
Print
Console.WriteLine ("The information you receive is" + data);
}
3.3 Synchronization problems in program blocking and Asynchrony
. NET provides a EventWaitHandle class to represent synchronization events for a thread. EventWaitHandle, an event-waiting handle, allows threads to communicate with each other through the operating system and wait for each other's signals to achieve thread synchronization. This class has 2 subclasses, namely autoresteevnt (auto reset) and manualrestevent (manual reset). Here are several ways to synchronize threads:
(1) RSet method: Sets the event state to a non signaled state, causing the thread to block. Thread blocking here means that the thread containing the WaitOne () method is blocked by allowing other threads that need to wait for blocking;
(2) Set method: Sets the event state to a signaled state, allowing one or more wait threads to continue. This method sends a signal to the operating system so that a thread that is waiting is converted from a blocking state to continue running, that is, the thread of the WaitOne method is not blocked;
(3) WaitOne method: Blocks the current thread until the current wait handle receives a signal. This method will keep this thread blocked until it receives a signal, that is, you can continue execution when other non-blocking processes call the Set method.
public static void Startlistening () {//data buffer for incoming data.
byte[] bytes = new byte[1024];
Establish the local endpoint for the socket.
The DNS name of the computer//running the listener is "host.contoso.com".
Iphostentry iphostinfo = Dns.resolve (Dns.gethostname ());
IPAddress ipaddress = iphostinfo.addresslist[0];
IPAddress ipaddress = Ipaddress.parse ("127.0.0.1");
IPEndPoint localendpoint = new IPEndPoint (ipaddress, 11000);
Create a TCP/IP socket.
Socket listener = new socket (addressfamily.internetwork,sockettype.stream, protocoltype.tcp);
Bind the socket to the "local//endpoint" and listen for incoming connections. try {Listener.
Bind (Localendpoint); Listener.
Listen (100);
while (true) {//Set the event to nonsignaled state.
Alldone.reset ();
Start a asynchronous socket to listen for connections.
Console.WriteLine ("Waiting for a Connection ..."); Listener. BeginAccept (New AsyncCallback (AcceptcallbaCK), listener);
Wait until a connection is made before continuing.
Alldone.waitone ();
The catch (Exception e) {Console.WriteLine (e.tostring ());
} Console.WriteLine ("\npress ENTER to continue ...");
Console.read ();
}
The logic of the preceding code is:
(1) The Manualrestevent object was tried to create a wait handle, and the rest method was used to allow other threads to block before calling the BeginAccept method;
(2) to prevent a socket from being read or written before the connection completes, it is important to call WaitOne after the BeginAccept method to allow the thread to enter the blocking state.
The callback function is invoked automatically when a connection is made, so when the code executes to the callback function, the connection is successful and the Set method is invoked in the first sentence of the function to allow the waiting thread to continue executing.
Iv. examples
The following is an instance where the client requests a connection, a server-side listening port, and when the connection is established, the server sends a string to the client, which is received and sent back to the server side.
Server-side code:
Using System;
Using System.Net;
Using System.Net.Sockets;
Using System.Text;
Using System.Threading;
State object for reading client data asynchronously the public class StateObject {//client socket.
Public Socket worksocket = null;
Size of receive buffer.
public const int buffersize = 1024;
Receive buffer.
Public byte[] buffer = new Byte[buffersize];
Received data string.
Public StringBuilder sb = new StringBuilder ();
The public class Asynchronoussocketlistener {//Thread signal.
public static ManualResetEvent alldone = new ManualResetEvent (false);
Public Asynchronoussocketlistener () {} public static void Startlistening () {//data buffer for incoming data.
byte[] bytes = new byte[1024];
Establish the local endpoint for the socket.
The DNS name of the computer//running the listener is "host.contoso.com".
Iphostentry iphostinfo = Dns.resolve (Dns.gethostname ());
IPAddress ipaddress = iphostinfo.addresslist[0]; Ipaddress ipaddress = Ipaddress.parse ("127.0.0.1");
IPEndPoint localendpoint = new IPEndPoint (ipaddress, 11000);
Create a TCP/IP socket.
Socket listener = new socket (addressfamily.internetwork,sockettype.stream, protocoltype.tcp);
Bind the socket to the "local//endpoint" and listen for incoming connections. try {Listener.
Bind (Localendpoint); Listener.
Listen (100);
while (true) {//Set the event to nonsignaled state.
Alldone.reset ();
Start a asynchronous socket to listen for connections.
Console.WriteLine ("Waiting for a Connection ..."); Listener.
BeginAccept (New AsyncCallback (Acceptcallback), listener);
Wait until a connection is made before continuing.
Alldone.waitone ();
The catch (Exception e) {Console.WriteLine (e.tostring ());
} Console.WriteLine ("\npress ENTER to continue ...");
Console.read (); The public static void Acceptcallback (IAsyncResult ar) {//Signal is the main thread to continue.
Alldone.set ();
Get the socket that handles the client request. Socket listener = (socket) ar.
asyncstate; Socket handler = listener.
Endaccept (AR);
Create the state object.
StateObject state = new StateObject ();
State.worksocket = handler; Handler.
BeginReceive (state.buffer, 0, stateobject.buffersize, 0, New AsyncCallback (Readcallback), state);
public static void Readcallback (IAsyncResult ar) {String content = String.Empty;
Retrieve the state object and the handler socket//from the asynchronous state object. StateObject state = (stateobject) ar.
asyncstate;
Socket handler = State.worksocket;
Read data from the client socket. int bytesread = handler.
EndReceive (AR);
if (Bytesread > 0) {//There might is more data, so far of the data received so.
State.sb.Append (Encoding.ASCII.GetString (state.buffer, 0, bytesread)); Check for End-of-file tag.
If It isn't there, read//More data. Content = State.sb.ToString (); if (content). IndexOf ("<EOF>") >-1) {//All of the data has been read from the//client.
Display it on the console. Console.WriteLine ("Read {0} bytes from socket.") \ n Data: {1} ", content.
Length, content);
Echo the data back to the client.
Send (handler, content); else {//Not all data received.
Get more. Handler.
BeginReceive (state.buffer, 0, stateobject.buffersize, 0, New AsyncCallback (Readcallback), state); }} private static void Send (Socket handler, string data) {//Convert the string data to byte using ASCII E
Ncoding.
byte[] Bytedata = Encoding.ASCII.GetBytes (data);
Begin sending the data to the remote device. Handler.
BeginSend (bytedata, 0, bytedata.length, 0, New AsyncCallback (Sendcallback), handler);
private static void Sendcallback (IAsyncResult ar) {try {//Retrieve the socket from the ' state object. Socket handler = (socket) ar.
asyncstate; Complete SENding the data to the remote device. int bytessent = handler.
Endsend (AR);
Console.WriteLine ("Sent {0} bytes to client.", bytessent); Handler.
Shutdown (Socketshutdown.both); Handler.
Close ();
catch (Exception e) {Console.WriteLine (e.tostring ());
} public static int Main (string[] args) {startlistening ();
return 0;
}
}
Client code:
Using System;
Using System.Net;
Using System.Net.Sockets;
Using System.Threading;
Using System.Text;
State object to receiving data from remote device.
public class StateObject {//Client socket.
Public Socket worksocket = null;
Size of receive buffer.
public const int buffersize = 256;
Receive buffer.
Public byte[] buffer = new Byte[buffersize];
Received data string.
Public StringBuilder sb = new StringBuilder ();
The public class Asynchronousclient {//The port number is for the remote device.
Private Const int port = 11000;
ManualResetEvent instances signal completion.
private static ManualResetEvent Connectdone = new ManualResetEvent (false);
private static ManualResetEvent Senddone = new ManualResetEvent (false);
private static ManualResetEvent Receivedone = new ManualResetEvent (false);
The response from the remote device.
private static String response = String.Empty; private static void Startclient () {//Connect to a remote DeviCe.
try {//Establish the remote endpoint for the socket.
The name of the//remote device is "host.contoso.com".
Iphostentry iphostinfo = dns.resolve ("user");
IPAddress ipaddress = iphostinfo.addresslist[0];
IPAddress ipaddress = Ipaddress.parse ("127.0.0.1");
IPEndPoint remoteep = new IPEndPoint (ipaddress, Port);
Create a TCP/IP socket.
Socket client = new socket (addressfamily.internetwork, SocketType.Stream, protocoltype.tcp);
Connect to the remote endpoint. Client.
BeginConnect (remoteEP, New AsyncCallback (Connectcallback), client);
Connectdone.waitone ();
Send test data to the remote device.
Send (Client, "This is a test<eof>");
Senddone.waitone ();
Receive the response from the remote device.
Receive (client);
Receivedone.waitone ();
Write the response to the console.
Console.WriteLine ("Response Received: {0}", Response);
Release the socket. Client. Shutdown (SocketshuTdown.
Both); Client.
Close ();
Console.ReadLine ();
catch (Exception e) {Console.WriteLine (e.tostring ());
The private static void Connectcallback (IAsyncResult ar) {try {//Retrieve the socket from the ' state object. Socket client = (socket) ar.
asyncstate;
Complete the connection. Client.
EndConnect (AR); Console.WriteLine ("Socket connected to {0}", client.
Remoteendpoint.tostring ());
Signal that the connection has been made.
Connectdone.set ();
catch (Exception e) {Console.WriteLine (e.tostring ());
private static void Receive (Socket client) {try {//Create the state object.
StateObject state = new StateObject ();
State.worksocket = client;
Begin receiving the data from the remote device. Client.
BeginReceive (state.buffer, 0, stateobject.buffersize, 0, New AsyncCallback (ReceiveCallback), state);
catch (Exception e) {Console.WriteLine (e.tostring ()); }} private static void REceivecallback (IAsyncResult ar) {try {//Retrieve the state object and the client socket//from the Asynch
Ronous state object. StateObject state = (stateobject) ar.
asyncstate;
Socket client = State.worksocket;
Read data from the remote device. int bytesread = client.
EndReceive (AR);
if (Bytesread > 0) {//There might is more data, so far of the data received so.
State.sb.Append (Encoding.ASCII.GetString (state.buffer, 0, bytesread));
Get the rest of the data. Client.
BeginReceive (state.buffer, 0, stateobject.buffersize, 0, New AsyncCallback (ReceiveCallback), state);
else {//All the data has arrived, put it in response.
if (State.sb.Length > 1) {response = state.sb.ToString ();
}//Signal that all bytes have been received.
Receivedone.set ();
The catch (Exception e) {Console.WriteLine (e.tostring ());
private static void Send (Socket client, String data) {//Convert the string data to byte using ASCII encoding.
byte[] Bytedata = Encoding.ASCII.GetBytes (data);
Begin sending the data to the remote device. Client.
BeginSend (bytedata, 0, bytedata.length, 0, New AsyncCallback (Sendcallback), client);
private static void Sendcallback (IAsyncResult ar) {try {//Retrieve the socket from the ' state object. Socket client = (socket) ar.
asyncstate;
Complete sending the data to the remote device. int bytessent = client.
Endsend (AR);
Console.WriteLine ("Sent {0} bytes to server.", bytessent);
Signal that all bytes have been sent.
Senddone.set ();
catch (Exception e) {Console.WriteLine (e.tostring ());
} public static int Main (string[] args) {startclient ();
return 0;
}
}
V. Results of the experiment
Figure 1 server-side interface
Figure 2 Client interface