The Real time Application is a sample that shows the communication techniques between a client (TcpClient) and a server (TcpServer) application using Socket class on each side. The project also demonstrates how to using listview control in the real time project.
- TcpServer.exe showing the use of TCP socket communication in a separate thread other than the Graphics User Interface (GUI) thread. Multiple instances of TcpClient can talk to the same instance of TcpServer.
- TcpClient.exe also uses a separate thread to read data from Socket then update the listview control in a form.
The flow of logic:
1. TcpServer listens on port 8002 and spawns a thread to waiting clients to connect.
Hashtable socketHolder = new Hashtable();
Hashtable threadHolder = new Hashtable();
public Form1()
{
// Required for Windows Form Designer support
//
InitializeComponent();
tcpLsn = new TcpListener(8002);
tcpLsn.Start();
// tcpLsn.LocalEndpoint may have a bug, it only show 0.0.0.0:8002
stpanel.Text = "Listen at: " + tcpLsn.LocalEndpoint.ToString();
Thread tcpThd = new Thread(new ThreadStart(WaitingForClient));
threadHolder.Add(connectId, tcpThd);
tcpThd.Start() ;
}
2. TcpClient connect to TcpSrv and sends Client information data packet to TcpServer then spawns a thread, which waits to receive data through the Socket.
private void menuConn_Click(object sender, System.EventArgs e)
{
ConnectDlg myDlg = new ConnectDlg();
myDlg.ShowDialog(this);
if( myDlg.DialogResult==DialogResult.OK)
s = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
IPAddress hostadd = IPAddress.Parse(myDlg.IpAdd);
int port=Int32.Parse(myDlg.PortNum);
IPEndPoint EPhost = new IPEndPoint(hostadd, port);
Try
{
s.Connect(EPhost);
if (s.Connected)
{
Byte[] bBuf;
string buf;
buf = String.Format("{0}:{1}", myDlg.UserName,
myDlg.PassWord);
bBuf=ASCII.GetBytes(buf);
s.Send(bBuf, 0 , bBuf.Length,0);
t = newThread(new ThreadStart(StartRecieve));
t.Start();
sbar.Text="Ready to recieve data";
}
}
catch(Exception e1)
{
MessageBox.Show(e1.ToString());
}
}
}
private void StartRecieve() {
MethodInvoker miv = new MethodInvoker(this.UpdateListView);
while (true) {
Byte[] receive = new Byte[38] ;
Try
{
// Receive will block until data coming
// ret is 0 or Exception happen when Socket connection is
// broken
int ret = s.Receive(receive, receive.Length, 0);
if (ret>0) {
tmp = System.Text.Encoding.ASCII.GetString(receive);
if(tmp.Length > 0)
{
isu.symbol= Mid(tmp, 0, 4);
isu.bid = Mid(tmp, 4, 5);
isu.offer = Mid(tmp, 9, 5);
isu.volume = Mid(tmp, 16, tmp.Length-16);
this.BeginInvoke(miv);
Thread.Sleep(300);
// block until finish the UpdateListviews job
JobDone.WaitOne();
}
}
}
catch (Exception e) {
if( !s.Connected )
{
break; }
} }
t.Abort();
}
3. TcpServer accepts the connection and saves the socket instance into a Hashtable instance then spawns a thread to handle the socket communication and show the client information in the top listview control.
public void WaitingForClient() { while(true) {
// Accept will block until someone connects
Socket sckt = tcpLsn.AcceptSocket();
if (connectId < 10000)
Interlocked.Increment(ref connectId);
Else
connectId = 1;
if (socketHolder.Count < MaxConnected )
{
while (socketHolder.Contains(connectId) )
{
Interlocked.Increment(ref connectId);
}
// it is used to keep connected Sockets
socketHolder.Add(connectId, sckt);
Thread td = new Thread(new ThreadStart(ReadSocket));
// it is used to keep the active thread
threadHolder.Add(connectId, td);
td.Start();
}
}
// follow function handle the communication and close the socket from the
// client side and close the thread when the socket connection is down
public void ReadSocket() {
// the connectId is keeping changed with new connection added. it can't
// be used to keep the real connectId, the local variable realId will
// keep the value when the thread started.
long realId = connectId;
int ind=-1;
Socket s = (Socket)socketHolder[realId];
while (true)
{
if(s.Connected)
{
Byte[] receive = new Byte[37] ;
Try
{
// Receive will block until data coming
// ret is 0 or Exception happen when Socket connection
// is broken
int ret=s.Receive(receive,receive.Length,0);
if (ret>0)
{
string tmp = null;
tmp=System.Text.Encoding.ASCII.GetString(receive);
if(tmp.Length > 0)
{
DateTime now1=DateTime.Now;;
String strDate;
string[] strArry=tmp.Split(':');
strDate = now1.ToShortDateString() + " "
+ now1.ToLongTimeString();
ListViewItem newItem = new ListViewItem();
//check user ID
if (!userHolder.ContainsValue(strArry[0]))
{
userHolder.Add(realId, strArry[0]);
newItem.SubItems.Add(strArry[0]);
newItem.ImageIndex = 0;
newItem.SubItems.Add(strDate);
this.listView2.Items.Add(newItem);
ind=this.listView2.Items.IndexOf(newItem);
}
else
{
string connFail = String.Format(
":The user {0} is connected already",
strArry[0]);
Byte[] byteData = ASCII.GetBytes(connFail.ToCharArray());
s.Send(byteData, byteData.Length,0);
s.Close();
socketHolder.Remove(realId);
break;
}
}
}
else
{
this.listView2.Items[ind].ImageIndex=1;
socketHolder.Remove(realId);
userHolder.Remove(realId);
break;
}
}
catch (Exception e)
{
if( !s.Connected )
{
this.listView2.Items[ind].ImageIndex=1;
socketHolder.Remove(realId);
userHolder.Remove(realId);
break;
}
}
}
}
Thread thd = (Thread)threadHolder[realId];
threadHolder.Remove(realId);
thd.Abort();
}
4. Click Load Data Menu to spawns a thread to load the information from a file then sends the information to all the clients that were connected to the TcpServer and update its own listview.
In both TcpServer and TcpClient, they get the data from a working thread, and then update the Listview control in the Main thread. Here use the MethodInvoker to work it out.
public void LoadThread()
{
MethodInvoker mi = new MethodInvoker(this.UpdateListView);
string tmp = null;
StreamReader sr = File.OpenText("Issue.txt");
while((tmp = sr.ReadLine()) !=null )
{
if (tmp =="")
break;
SendDataToAllClient(tmp);
isu.symbol= Mid(tmp, 0, 4);
isu.bid = Mid(tmp, 4, 5);
isu.offer = Mid(tmp, 9, 5);
isu.volume = Mid(tmp, 16, tmp.Length-16);
this.BeginInvoke(mi);
Thread.Sleep(200);
JobDone.WaitOne();
}
sr.Close();
fThd.Abort();
}
private void SendDataToAllClient(string str) {
foreach (Socket s in socketHolder.Values)
{
if(s.Connected)
{
Byte[] byteDateLine=ASCII.GetBytes(str.ToCharArray());
s.Send(byteDateLine, byteDateLine.Length, 0);
}
}
}
Following function demonstrate how to dynamically set BackColor and Forecolor properties of the Listview in TcpClient.
private void UpdateListView()
{
int ind=-1;
for (int i=0; i<this.listView1.Items.Count;i++)
{
if (this.listView1.Items[i].Text == isu.symbol.ToString())
{
ind=i;
break;
}
}
if (ind == -1)
{
ListViewItem newItem new ListViewItem(isu.symbol.ToString());
newItem.SubItems.Add(isu.bid);
newItem.SubItems.Add(isu.offer);
newItem.SubItems.Add(isu.volume);
this.listView1.Items.Add(newItem);
int i=this.listView1.Items.IndexOf(newItem);
setRowColor(i, System.Drawing.Color.FromArgb(255, 255, 175));
setColColorHL(i, 0, System.Drawing.Color.FromArgb(128,0,0));
setColColorHL(i, 1, System.Drawing.Color.FromArgb(128,0,0));
this.listView1.Update();
Thread.Sleep(300);
setColColor(i, 0, System.Drawing.Color.FromArgb(255, 255,175));
setColColor(i, 1, System.Drawing.Color.FromArgb(255, 255, 175));
}
else
{
this.listView1.Items[ind].Text = isu.symbol.ToString();
this.listView1.Items[ind].SubItems[1].Text = (isu.bid);
this.listView1.Items[ind].SubItems[2].Text = (isu.offer);
this.listView1.Items[ind].SubItems[3].Text = (isu.volume);
setColColorHL(ind, 0, System.Drawing.Color.FromArgb(128,0,0));
setColColorHL(ind, 1, System.Drawing.Color.FromArgb(128,0,0));
this.listView1.Update();
Thread.Sleep(300);
setColColor(ind, 0, System.Drawing.Color.FromArgb(255,255,175));
setColColor(ind, 1, System.Drawing.Color.FromArgb(255,255,175));
}
JobDone.Set();
}
private void setRowColor(int rowNum, Color colr )
{
for (int i=0; i<this.listView1.Items[rowNum].SubItems.Count;i++)
if (rowNum%2 !=0)
this.listView1.Items[rowNum].SubItems[i].BackColor = colr;
}
private void setColColor(int rowNum, int colNum, Color colr )
{
if (rowNum%2 !=0)
this.listView1.Items[rowNum].SubItems[colNum].BackColor=colr;
else
this.listView1.Items[rowNum].SubItems[colNum].BackColor =
System.Drawing.Color.FromArgb(248, 248,248);
if (colNum==0)
{
this.listView1.Items[rowNum].SubItems[colNum].ForeColor =
System.Drawing.Color.FromArgb(128, 0, 64);
this.listView1.Items[rowNum].SubItems[colNum].BackColor =
System.Drawing.Color.FromArgb(197, 197, 182);
}
else
this.listView1.Items[rowNum].SubItems[colNum].ForeColor =
System.Drawing.Color.FromArgb(20, 20,20);
}
private void setColColorHL(int rowNum, int colNum, Color colr )
{
this.listView1.Items[rowNum].SubItems[colNum].BackColor = colr;
this.listView1.Items[rowNum].SubItems[colNum].ForeColor =
System.Drawing.Color.FromArgb(255,255,255);
}
Steps to run the sample:
- Run TcpServer.exe on machine A.
- Run TcpClient.exe once or more either on machine A or machine B.
- On the TcpClient side, Click Menu connect; enter the server machine name where TcpServer is running. Enter user name and password in the edit box. Click Ok.
- When you see the client in the TcpServer top listview, click Load Data Menu on the TcpServer, and then you will see the real time data in TcpServer and TcpClient.
Note: Make sure that the Data file, Issue.txt, is in the same directory as TcpSvr.exe.
If you have any comments, I would love to hear about it. You can reach me at Jibin Pan.
This is sample text of the article.
HEADING
Table format for code
public class HelloWorld
{
public static void Main()
{
System.Console.WriteLine("HELLO WORLD");
}
}