Use non-blocking Socket Communication

Source: Internet
Author: User
Asynchronous Socket Communication
By John mctainsh
From:
Http://www.codeproject.com/csharp/socketsincs.asp
Translate by: hillfree

This article describes how to use a non-blocking Socket Communication and creates an example of a chat program.IntroductionThis article describes how to create and use TCP/IP socket for communication between multiple applications. These applications can run on the same machine, on the LAN, or even across the Internet *. The advantage of this method is that you do not need to use the thread by yourself, but call the non-blocking mode of socket. In this example, the server creates a connection to the disease listening client. Once a customer connects, the server adds the connection to the list of active customers, messages sent from a client are also sent from the server to various connected clients, as in the chat room. Maybe remoting (Remote Call) is a better way to do this, but here we will learn how to use socket to implement it. * Note: communication over the Internet requires the server to have an independent IP address and is not behind the proxy or the firewall.Event SequenceThe server must listen before the client can connect. The following example illustrates the Event Sequence in an asynchronous socket session.Running exampleThe instance code is divided into two parts: chatserver and chatclient. We will first create the chatserver, and then use the following Telnet command to test it.

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 client connection address and port. The characters typed in any Telnet window are displayed in all Telnet windows connected to the server. Try connecting to the server concurrently 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 for the same test and multiple clients and multiple Telnet coexist tests. Why use. Net socket?. Net uses sockets in many places, such as WebServices and remoting. However, the underlying socket support for those applications is ready and does not need to be used directly. However, it is necessary to use sockets in non-. Net System socket processing or simple communication scenarios. It can be used to communicate with systems such as DOS, windows, and UNIX. The underlying Socket Application can also reduce security concerns such as group tests, permissions, domains (domains), user IDs, and passwords. Chatserver/listenerThe listening port of the server. When there is a connection request, accept the connection and return a welcome message. In this example, the customer connection is added to an active customer list. m_aryClients. This list will be added or deleted based on the customer's addition and departure. In some cases, the connection may be lost, so in the actual system, there should also be a portion of the polling to detect whether the client is online. When the listener on the server receives the message from the client, it broadcasts the message to all connected clients. The following describes two listener Methods: polling and event detection. Method 1: Use the polling tcplistener System.Net.SocketsIn TcpListenerClass provides us with a simple way to listen and process customer connections. The following code listens for connections, accepts connections, and sends a timestamp Welcome Message to the customer connection. If another connection request arrives, the original connection will be lost. Note that the welcome message uses ASCII encoding instead of 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];
        if( listener.Pending() )
        {
            client = listener.AcceptSocket();
            // Get current date and time.
            DateTime now = DateTime.Now;
            string strDateLine = "Welcome " + now.ToString("G") + "/n/r";
 
            // Convert to byte array and send.
            Byte[] byteDateLine = System.Text.Encoding.ASCII.GetBytes( strDateLine.ToCharArray() );
            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-Use a socket with an eventA more elegant method is to create an event to capture connection requests. The chatserver instance adopts this method. First, use the following code to obtain the server name and address.
IPAddress [] aryLocalAddr = null;
string strHostName = "";
try
{
    // NOTE: DNS lookups are nice and all but quite time consuming.
    strHostName = Dns.GetHostName();
    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 obtaining the address, we need to bind the socket listener to this address. The listening port we use here is 399. In addition, reading the port number from the service file "C:/winnt/system32/Drivers/etc/services" is a good exercise. The following code binds the listener and starts listening. An event handler directs all connection requests to onconnectrequest. In this way, the program can process customer connections 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 IPEndPoint( 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.OnConnectRequest ), listener );
When the client connection request arrives, the following processing events will be triggered. The following code first creates a client (socket), then sends back the welcome information, and then creates an accept event handler ).
Socket client;
public void OnConnectRequest( IAsyncResult ar )
{
    Socket listener = (Socket)ar.AsyncState;
    client = listener.EndAccept( ar );
    Console.WriteLine( "Client {0}, joined", client.RemoteEndPoint );
    // Get current date and time.
    DateTime now = DateTime.Now;
    string strDateLine = "Welcome " + now.ToString("G") + "/n/r";
 
    // Convert to byte array and send.
    Byte[] byteDateLine = System.Text.Encoding.ASCII.GetBytes( strDateLine.ToCharArray() );
    client.Send( byteDateLine, byteDateLine.Length, 0 );
 
    listener.BeginAccept( new AsyncCallback( OnConnectRequest ), listener );
}
This code can be expanded to maintain the customer's socket list, monitoring data reception and disconnection. For connection disconnection detection AsyncCallbackThe event is being processed. The chatclient section will detail this mechanism below. ChatclientChatclient is a Windows form application that connects to the server and sends and receives messages. ConnectionClick the connection button on the interface to execute the following program to connect the customer 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, destroy it. Create a socket to connect to the specified endpoint. Code that has been commented out adopts a simple blocking connection method. BeginConnectIs used for a non-blocking connection request. Note: even for a non-blocking user connection request, the connection is blocked and the machine name is resolved as an IP address. Therefore, try to use IP addresses instead of machine names to avoid this situation. Once the connection request is processed, the following method is called. It indicates a connection error or a callback for receiving data is established when the connection is successful.
public void OnConnect( IAsyncResult ar )
{
    // Socket was the passed in object
    Socket sock = (Socket)ar.AsyncState;
 
    // Check if we were sucessfull
    try
    {
        //    sock.EndConnect( ar );
        if( sock.Connected )
            SetupRecieveCallback( sock );
        else
            MessageBox.Show( this, "Unable to connect to remote machine", 
                             "Connect Failed!" );
 
    }
    catch( Exception ex )
    {
        MessageBox.Show( this, ex.Message, "Unusual error during Connect!" );
    }    
}
Receive dataIn order to receive data asynchronously, it is necessary to establish AsyncCallbackTo process events that are triggered by data connection and 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!" );
    }
}
SetupRecieveCallbackMethod started BeginReceiveAnd use the proxy pointer to direct the callback OnReceveDataMethod. It also transmits a buffer 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 callback
            SetupRecieveCallback( sock );
        }
        else
        {
            // If no data was recieved then the connection is probably 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 preceding event is triggered, the received data is ASCII encoded by default. New data is also displayed as a triggered event. Although it can be called Add()It is not a good idea to display new data in the list, because the received data is likely to be sent to other threads for processing. Note: You need to re-receive the callback after receiving it to ensure that you can continue to receive data. Because there may be a lot of data that exceeds the initial buffer capacity. Create AddMessageDelegation can reduce the coupling between socket threads and user interface threads, as shown below:
// Declare the delegate prototype to send data back to the form
delegate void AddMessage( string sNewMessage );
 
namespace ChatClient
{
    . . .
    public class FormMain : 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 OnSomeOtherThread()
        {
            . . .
            string sSomeText = "Bilbo Baggins";
            Invoke( m_AddMessage, new string [] { sSomeText } );
        }
        . . .
    }    
}
Use UnicodeWhen bit streams are used to send and receive data, the data needs to be properly encoded. C # multi-byte character encoding, although Encoding.ASCII, But you can also use Encoding.UNICODE Don't trust what you send to receive anything.When receiving data events is triggered, the received data is placed in the receiving buffer. In our development, Group Sending usually corresponds to a group receiving event. But not in real systems. Data is not always in the correct text, but may be split into several groups. Don't expect to always receive the complete message, or expect to build your own symbol to mark the beginning and end of the message. ConclusionAlthough it is not difficult to use socket, it still requires a lot of practical exercises. Of course, you should also try using WebServices or remoting in the appropriate scenarios. In addition, wrox's professional ADO. NET programming book is good and worth reading.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.