Click here to Skip to main content
11,490,292 members (51,733 online)
Click here to Skip to main content
Add your own
alternative version

Asynchronous socket communication

, 27 Nov 2001 536.8K 15.9K 390
An article on using sockets to communicate in a non-blocking manner. The sample works through building a simple chat client and server.
socketsincs_demo.zip
ChatClient.exe
ChatServer.exe
socketsincs_src.zip
cs
Chat
ChatClient
bin
Debug
ChatClient.csproj.user
licenses.licx
obj
Debug
temp
TempPE
ChatServer
ChatServer.csproj.user
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;									// Endpoint
using System.Net.Sockets;							// Socket namespace
using System.Text;									// Text encoders

// Declare the delegate prototype to send data back to the form
delegate void AddMessage( string sNewMessage );


namespace ChatClient
{
	/// <summary>
	/// This form connects to a Socket server and Streams data to and from it.
	/// Note: The following has been ommitted.
	///		1) Send button need to be grayed when conneciton is 
	///		   not active
	///		2) Send button should gray when no text in the Message box.
	///		3) Line feeds in the recieved data should be parsed into seperate
	///		   lines in the recieved data list
	///		4) Read startup setting from a app.config file
	/// </summary>
	public class FormMain : System.Windows.Forms.Form
	{
		// My Attributes
		private Socket			m_sock;						// Server connection
		private byte []			m_byBuff = new byte[256];	// Recieved data buffer
		private event AddMessage m_AddMessage;				// Add Message Event handler for Form

		// Wizard generated code
		private System.Windows.Forms.Button m_btnConnect;
		private System.Windows.Forms.TextBox m_tbServerAddress;
		private System.Windows.Forms.ListBox m_lbRecievedData;
		private System.Windows.Forms.TextBox m_tbMessage;
		private System.Windows.Forms.Button m_btnSend;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public FormMain()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			// Add Message Event handler for Form decoupling from input thread
			m_AddMessage = new AddMessage( OnAddMessage );


			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.m_tbServerAddress = new System.Windows.Forms.TextBox();
			this.m_tbMessage = new System.Windows.Forms.TextBox();
			this.m_btnConnect = new System.Windows.Forms.Button();
			this.m_lbRecievedData = new System.Windows.Forms.ListBox();
			this.m_btnSend = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// m_tbServerAddress
			// 
			this.m_tbServerAddress.Anchor = ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right);
			this.m_tbServerAddress.Location = new System.Drawing.Point(8, 8);
			this.m_tbServerAddress.Name = "m_tbServerAddress";
			this.m_tbServerAddress.Size = new System.Drawing.Size(204, 20);
			this.m_tbServerAddress.TabIndex = 1;
			this.m_tbServerAddress.Text = "192.168.0.8";
			// 
			// m_tbMessage
			// 
			this.m_tbMessage.Anchor = ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right);
			this.m_tbMessage.Location = new System.Drawing.Point(8, 37);
			this.m_tbMessage.Name = "m_tbMessage";
			this.m_tbMessage.Size = new System.Drawing.Size(205, 20);
			this.m_tbMessage.TabIndex = 3;
			this.m_tbMessage.Text = "";
			// 
			// m_btnConnect
			// 
			this.m_btnConnect.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right);
			this.m_btnConnect.Location = new System.Drawing.Point(228, 8);
			this.m_btnConnect.Name = "m_btnConnect";
			this.m_btnConnect.TabIndex = 0;
			this.m_btnConnect.Text = "Connect";
			this.m_btnConnect.Click += new System.EventHandler(this.m_btnConnect_Click);
			// 
			// m_lbRecievedData
			// 
			this.m_lbRecievedData.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
				| System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right);
			this.m_lbRecievedData.IntegralHeight = false;
			this.m_lbRecievedData.Location = new System.Drawing.Point(0, 66);
			this.m_lbRecievedData.Name = "m_lbRecievedData";
			this.m_lbRecievedData.Size = new System.Drawing.Size(311, 220);
			this.m_lbRecievedData.TabIndex = 2;
			// 
			// m_btnSend
			// 
			this.m_btnSend.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right);
			this.m_btnSend.Location = new System.Drawing.Point(228, 36);
			this.m_btnSend.Name = "m_btnSend";
			this.m_btnSend.TabIndex = 4;
			this.m_btnSend.Text = "Send";
			this.m_btnSend.Click += new System.EventHandler(this.m_btnSend_Click);
			// 
			// FormMain
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(312, 287);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.m_btnSend,
																		  this.m_tbMessage,
																		  this.m_lbRecievedData,
																		  this.m_tbServerAddress,
																		  this.m_btnConnect});
			this.Name = "FormMain";
			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
			this.Text = "Form1";
			this.Closing += new System.ComponentModel.CancelEventHandler(this.FormMain_Closing);
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new FormMain());
		}

		/// <summary>
		/// Connect button pressed. Attempt a connection to the server and 
		/// setup Recieved data callback
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		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;
		}

		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!" );
			}
		}

		/// <summary>
		/// Get the new data and send it out to all other connections. 
		/// Note: If not data was recieved the connection has probably 
		/// died.
		/// </summary>
		/// <param name="ar"></param>
		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!" );
			}
		}

		public void OnAddMessage( string sMessage )
		{
			m_lbRecievedData.Items.Add( sMessage );
		}
		


		/// <summary>
		/// Setup the callback for recieved data and loss of conneciton
		/// </summary>
		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!" );
			}
		}

		/// <summary>
		/// Close the Socket connection bofore going home
		/// </summary>
		private void FormMain_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			if( m_sock != null && m_sock.Connected )
			{
				m_sock.Shutdown( SocketShutdown.Both );
				m_sock.Close();
			}
		}

		/// <summary>
		/// Send the Message in the Message area. Only do this if we are connected
		/// </summary>
		private void m_btnSend_Click(object sender, System.EventArgs e)
		{
			// Check we are connected
			if( m_sock == null || !m_sock.Connected )
			{
				MessageBox.Show( this, "Must be connected to Send a message" );
				return;
			}

			// Read the message from the text box and send it
			try
			{		
				// Convert to byte array and send.
				Byte[] byteDateLine = Encoding.ASCII.GetBytes( m_tbMessage.Text.ToCharArray() );
				m_sock.Send( byteDateLine, byteDateLine.Length, 0 );
			}
			catch( Exception ex )
			{
				MessageBox.Show( this, ex.Message, "Send Message Failed!" );
			}
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

John McTainsh

United States United States
I starting writing code when Apple IIe was all the rage (1982?). Graduated Mechanical Engineering in New Zealand, so I could build Robots. Took up Commerical diving in Asia for a few years to make some quick money (Spent it all). I have been writing C/C++/Assembly and building underwater robots since MS QuickC/Quick Assembler in 1989. I currently live in Australia and work mosly with MS VisualC.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150520.1 | Last Updated 28 Nov 2001
Article Copyright 2001 by John McTainsh
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid