Asynchronous Socket Communication by John Mctainsh from: http://www.codeproject.com/csharp/socketsincs.asptranslate by: Hillfree This article describes how to use a non-blocking Socket communication, and create an example of a chat program to help instructions.
Introduction
This article describes how to create and use TCP / IP Socket between multiple applications to communicate. These applications can run in the same machine or within the local area network, or even throws across the Internet. The advantage of this method is not to use threads yourself, but is implemented by calling the Non-blocking mode of Socket. In the example: The server creates a connection to the client's connection. Once there is a customer connection, the server adds it to a list of active customers, and the message sent by a client also has a server sent to each connected client, as if The chat room is in the chat room. Perhaps Remoting (Remote Call) is a better way to do this, but we still have to learn how to use Socket to implement.
* Note: Communication across the Internet requires the server to have a separate IP address and not after the agent or the fire wall.
Event timing
The server must be listened first, and the client can connect. The following graph illustrates the event timing in an asynchronous Socket session.
Run example
The instance code is divided into two parts: ChatServer and chatclient. We first create ChatServer, then test it with the telnet command below.
Telnet {Server Machine IP Address or Machine Name} 399
Telnet 10.328.32.76 399
At this time, a message should appear on the server to indicate the address and port of this client. The characters typed in any Telnet window will look back in all windows of all Telnet connected to the server. Try to connect the server from multiple machines. Do not use localhost or 127.0.0.1 as the unique listening address of the server program.
Then run the ChatClient instance as the same test and multiple clients and multiple Telnet coexistence tests.
Why use .NET's Socket?
.NET uses Sockets in many places, such as WebServices and Remoting. But in the bottom of the bottom of the app, it is already done, and it is not necessary to use it directly. However, the use of Socket is still necessary in the case of other non-.NET systems to deal or simple communication or simple communication. It can be used to communicate with such as DOS, Windows, and UNIX systems. The underlying Socket application can also reduce the security of these troubles such as group testing, permissions, domains, user ID, passwords.
ChatServer / Listener
Server listening ports When there is a connection request, accept the connection and return a welcome information. In the example, the client connection is added to an active customer list M_aryClients. This list will be deleted accordingly according to the customer's addition and leave. In some cases, the connection may be lost, so in the actual system, there should be a part of the polling detection client online. When the server-side Listener receives the information sent by the client, it broadcasts the message to all connected clients.
The two listening methods are discussed below, one is a polling, another request to detect the connection in use.
Method 1 - use polling TCPListener
The TCPListener class in System.Net.Sockets provides us with a simple means of listening and handling customer connections. The following code listens on the connection, accepts the connection, and send back a welcome information with a timestamp to the customer connection. If there is another connection request, the original connection will be lost. Note that the welcome information is an ASCII code, not Unicode. PRIVATE SOCKET Client = NULL;
Const int nportlisten = 399;
Try
{
TCPListener Listener = New TCPListener (NportListen);
Console.writeline ("Listening as {0}", Listener.localendPoint;
Listener.start ();
DO
{
Byte [] M_BYBUFF = New byte [127];
Listener.pending ())
{
Client = listener.acceptsocket ();
// Get Current Date and Time.
DateTime now = datetime.now;
String stradateline = "welcome" now.toT7tring ("g") "/ n / r";
// convert to byte array and send.
Byte [] bytateline = system.text.encoding.ascii.getbytes (STRDATELINE.TOCHARRAY ());
Client.send (Bytedateline, BytedateLine.Length, 0);
}
Else
{
Thread.sleep (100);
}
WHILE (TRUE); // Don't Use this.
}
Catch (Exception EX)
{
Console.writeLine (ex.Message);
}
Method 2 - Socket with events
A more elegant approach is to create an event to capture the connection request. This method is used by the ChatServer instance. First, the name and address of the server are obtained with the code below.
Ipaddress [] ARYLOCALADDR = NULL;
String strHostname = ""
Try
{
// NOTE: DNS Lookups Are Nice And All But Quite Time Consuming.
Strhostname = DNS.GETOSTNAME ();
IPHOSTENTRY IPENTRY = DNS.GETHOSTBYNAME (STRHOSTNAME);
Arylocaladdr = IpenTry.AddressList;
}
Catch (Exception EX)
{
Console.Writeline ("Error Trying to get local address {0}", ex.Message);
}
// Verify We got an ip address. Tell The User IF WE DID
IF (AryLocaladdr == Null || Arylocaladdr.Length <1)
{
Console.WriteLine ("Unable to get local address";
Return;
}
Console.Writeline ("Listening On: [{0}] {1}", strHostname, AryLocaladdr [0]); after getting the address, we have to bind the listener this socket to this address. The listening port we used here is 399. In addition, read the port number from the "C: / WinNT / System32 / Drivers / etc /" "service file should be a good practice. The following code binds Listener and start listening. An event handler points all the connection requests to ONCONNECTREQUEST. This program can handle the customer connection without waiting or polling.
Const int nportlisten = 399;
// Create The Listener Socket in this Machines IP Address
Socket Listener = New Socket (AddressFamily.internetwork, sockettype.Stream, protocoltype.tcp);
Listener.bind (New IpendPoint (Arylocaladdr [0], 399));
//listener.bind (new iPadpoint (iPaddress.loopback, 399)); // for use with localhost 127.0.0.1
Listener.listen (10);
// setup a callback to be notified of connection requests
Listener.BeginAccept (new asyncCallback (app.onnectRequest), Listener;
When the client connection request arrives, the following processing event will be inspired. The following code first creates a client (socket), then sending a welcome information, then re-establishing an event handle (Accept Event Handler).
Socket client;
Public void OnConnectRequest (IasyncResult Ar)
{
Socket Listener = (socket) ar.asyncstate;
Client = Listener.ndAccept (ar);
Console.WriteLine ("Client {0}, Joined", Client.RemoteEndPoint;
// Get Current Date and Time.
DateTime now = datetime.now;
String stradateline = "welcome" now.toT7tring ("g") "/ n / r";
// convert to byte array and send.
Byte [] bytateline = system.text.encoding.ascii.getbytes (STRDATELINE.TOCHARRAY ());
Client.send (Bytedateline, BytedateLine.Length, 0);
Listener.BeginAccept (New AsyncCallback (onConnectRequest), Listener;
}
This code can be extended, maintaining a list of client sockets, monitoring data reception and connection disconnection. For the connection disconnected detection is placed in the AsyncCallback event processing. This mechanism will be described below in the ChatClient section.
ChatClient
ChatClient is a Windows Form application to connect to the server, send and receive messages. connection
When the connection button on the interface makes the customer to connect the client to the server.
PRIVATE SOCKET M_SOCK = NULL;
Private void m_btnconnect_click (Object Sender, System.Eventargs E)
{
CURSOR CURSOR = Cursor.current;
Cursor.current = cursors.waitcursor;
Try
{
// Close the socket if it is still open
IF (m_sock! = null && m_sock.connected)
{
m_sock.shutdown (socketshutdown.both);
System.Threading.Thread.sleep (10);
m_sock.close ();
}
// Create the socket Object
m_sock = new socket (addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
// define the server address and port
IpendPoint EPSERVER = New IpendPoint (ipaddress.parse (m_tbserveraddress.text), 399);
// Connect To the Server Blocking Method and Setup Callback for Recieved Data
// m_sock.connect (epserver);
// setupRecieveCallback (m_sock);
// Connect To Server Non-Blocking Method
m_sock.blocking = false;
AsyncCallback onConnect = New AsyncCallback (onConnect);
M_Sock.BeginConnect (EPSERVER, ONCONNECT, M_SOCK);
}
Catch (Exception EX)
{
Messagebox.show (this, ex.Message, "Server Connect Failed!");
}
Cursor.current = CURSOR;
}
If the connection already exists, it is destroyed. Create a socket and the specified endpoint. The code that is commented out is a simple blocked connection method. BeginConnect is used to make a non-blocking connection request. Note that even a non-blocking user connection request, the connection is also blocked by the machine name is parsed to the IP address. So, try to use the IP address instead of the machine name to avoid this. Once the connection request is processed, the following method will be called, which displays the connection error or establish a callback that receives data in the case of successful connection.
Public void OnConnect (IasyncResult Ar)
{
// Socket Was The Passed In Object
Socket Sock = (socket) ar.asyncState;
// Check if We were successfull
Try
{
// sock.endconnect (ar);
IF (sock.connected)
SetupRecieveCallback (SOCK);
Else
Messagebox.show (this, "Unable to connection remote machine," connect failed! ");" Connect Failed! ");
}
Catch (Exception EX)
{
MessageBox.show (this, ex.Message, "Unusual Error During Connect!);
}
}
Receive data
For asynchronous reception data, it is necessary to establish an ASYNCCALLBACK to process an event that is excited by the data and connection disconnection. Use the following method.
Private Byte [] m_bybuff = new byte [256]; // Recieved Data Buffer
Public Void SetupRecieveCallback (Socket Sock)
{
Try
{
AsyncCallback recievedata = new asyncCallback (OnRecievedData);
Sock.beginReceive (m_bybuff, 0, m_bybuff.length, socketflags.none,
Recievedata, SOCK;
}
Catch (Exception EX)
{
Messagebox.show (this, ex.Message, "Setup Recieve Callback Failed!");
}
}
The SetupRecieveCallback method launches BeginReceive and uses the proxy pointer to point the callback to the OnRecevedata method. At the same time, it also passes a buffering of data to receive data.
Public void onRecievedData (IasyncResult Ar)
{
// Socket Was The Passed In Object
Socket Sock = (socket) ar.asyncState;
// Check if We got any data
Try
{
INT NBYTESREC = SOCK.Endreceive (ar);
IF (NbytesRec> 0)
{
// wrote the data to the list
String Srecieved = encoding.ascii.getstring (m_bybuff, 0, nbytesRec);
// Warning: The Following Line is Not thread Safe. Invoke IS
// m_lbrecievedData.Items.Add (SRecieved);
Invoke (m_addmessage, new string [} {srecieved});
// if the connection is still usable restablish the callack
SetupRecieveCallback (SOCK);
}
Else
{
// if no data was recieved the connection is probly dead
Console.Writeline ("Client {0}, disconnected", sock.remoteendpoint;
Sock.shutdown (socketshutdown.both);
Sock.close ();
}
}
Catch (Exception EX)
{
Messagebox.show (this, ex.Message, "Unusual Error Druing Recieve!");
}
}
When the above event is excited, the received data is default that is ASCII encoded. New data will also be excited. Although you can call Add () to display new data in the list, this is not a good idea, because the received data is likely to be sent to other threads. Note that you need to reconstruct the received callback after receiving to ensure that the data can be continued. Because there are many possible data, more than the original Buffer capacity. Creating a AddMessage Privilege can reduce the degree of coupling of the socket thread and the user interface thread, as shown below:
// Declare the delegate prototype to send data back to the form
Delegate Void AddMessage (String SnewMessage);
Namespace chatclient
{
.
Public Class Formrain: System.Windows.Forms.form
{
PRIVATE EVENT AddMessage M_AddMessage;
// Add Message Event Handler for Form
.
Public Formmain ()
{
.
// Add Message Event Handler for Form Decoupling from Input Thread
m_addMessage = New AddMessage (onaddMessage);
.
}
Public void onaddMessage (String SMESSAGE)
{
// Thread Safe Operation Here
M_LbRecievedData.Items.Add (SMESSAGE);
}
Public void OnsomeTherthread ()
{
.
String ssometext = "bilbo baggins";
Invoke (m_addmessage, new string [] {ssometext});
}
.
}
}
Use Unicode
When the data is transmitted with a bit stream, the data needs to be properly encoded. C # uses multi-byte character encoding, although Encoding.ascii is used here, but if you need to use Encoding.Unicode
Don't believe what you can receive
When the received data event is excited, the received data is placed in the reception buffer. In our development, the grouping is often corresponding to a packet receiving event. But not this in a real system. Data is not all in the messages of regulations, and may be split into several packets. Don't expect to always receive complete packets, don't expect to build your own symbol tag packets, don't know.
in conclusion
Although it is not difficult to use Socket, it is very good to use a lot of practical practice. Of course, you should also try using WebServices or Remoting in a suitable occasion. In addition, the Professional ADO.NET Programming of the Wrox Press is very good, it is worth seeing.