Click here to Skip to main content
15,894,546 members
Articles / Programming Languages / C#
Article

Real Time TCP/IP using C#

Rate me:
Please Sign up or sign in to vote.
3.09/5 (20 votes)
12 Jan 20022 min read 579.6K   14.5K   108   42
This sample shows the communication techniques between a client and a server application using a Socket class on each side.

Introduction

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. 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 = new Thread(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  
            {    
                string tmp=null;             
                // 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
                        // UpdateListview’s 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 from the clients and close the    
    // socket and 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;                           
                                        strDate = now1.ToShortDateString() + " "  
                                                    + now1.ToLongTimeString();    
                                                                                  
                                        ListViewItem newItem = new ListViewItem();
                                        string[] strArry=tmp.Split(':');          
                                        int code = checkUserInfo(strArry[0]);     
                                        if(code==2)                               
                                        {                                         
                                              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 if( code==1)                         
                                                                                  
                                              ……………                               
                                  }                                               
                            }                                                     
                            else                                                  
                            {                                                     
                                  this.listView2.Items[ind].ImageIndex=1;         
                                  keepUser=false;                                 
                                  break;                                          
                            }                                                     
                      }                                                           
                      catch (Exception e)                                         
                      {                                                           
                            if( !s.Connected )                                    
                            {                                                     
                                  this.listView2.Items[ind].ImageIndex=1;         
                                  keepUser=false;                                 
                                  break;                                          
                            }                                                     
                      }                                                           
                }                                                                 
          }                                                                       
          CloseTheThread(realId);                                                 
    }                                                                             
    private void CloseTheThread(long realId)                                      
    {                                                                             
          socketHolder.Remove(realId);                                            
          if(!keepUser) userHolder.Remove(realId);                                
          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:

  1. Run TcpServer.exe on machine A.

  2. Run TcpClient.exe once or more either on machine A or machine B.

  3. 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.

  4. 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.

Jibin Pan is VC++, C programmer at Interactive Edge Corp. Xtend Communications Corp. MoneyLine Corp in New York City since 1994 and has Master degree at computer science.

History

13 Jan 2002 - updated source.

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Hi, I have read your thread, and....a prb Pin
Alex Korchemniy23-Dec-02 13:56
Alex Korchemniy23-Dec-02 13:56 
GeneralTerrible! Pin
Ian Griffiths14-Jan-02 10:32
Ian Griffiths14-Jan-02 10:32 
GeneralRe: Terrible! Pin
22-Jan-02 3:02
suss22-Jan-02 3:02 
GeneralRe: Terrible! Pin
Ian Griffiths22-Jan-02 5:26
Ian Griffiths22-Jan-02 5:26 
GeneralRe: Terrible! Pin
22-Jan-02 9:43
suss22-Jan-02 9:43 
GeneralRe: Terrible! Pin
Ian Griffiths22-Jan-02 23:27
Ian Griffiths22-Jan-02 23:27 
GeneralRe: Terrible! Pin
25-Jan-02 6:45
suss25-Jan-02 6:45 
GeneralRe: Terrible! Pin
Ian Griffiths28-Jan-02 4:59
Ian Griffiths28-Jan-02 4:59 
-- First, I don't like your idea that you don't do the clean job
-- and let the operating system to do it for you. If the system
-- have a bug then your program have the bug. You can expect the
-- system is bug free,

Of course no system is bug free, but do you have any reason to suppose that a bug of the nature you describe will emerge? Right now, threads terminate when you return from their ThreadStart function. It would be a pretty serious bug if this stopped working, because it would mean that most programs that launch threads would suddenly start to suffer from serious performance problems as the number of threads in the process stacked up uncontrollably.

So if Microsoft ever *did* manage to introduce such a bug into a future version by mistake, it would break a huge amount of code. Yours is the only program I have ever seen that anticipates such a bug in the runtime, so the vast majority of multithreaded programs would break. This suggests that such a release wouldn't make it through beta testing.

So don't you think you're being a bit paranoid? And even if you do think that it's appropriate to presume that such random and major bugs will appear in future releases, why are you any more confident that aborting the thread will work? If you don't have any faith that Microsoft won't break System.Thread in such a way that it doesn't terminate threads when they exit, why do you have any more faith that System.Thread.Abort won't also be broken in a future release?

Working around known bugs is fine, and standard practice. Putting in workarounds for bugs that have never existed, and which you have no good reason to suppose will *ever* exist is pointless, because if you take that approach to its logical conclusion, you'll never trust the platform to do anything at all!



-- I find the link for the update control from background
-- thread on the Web. I have said that "you can learn something
-- from here".
-- is not from me, is from Microsoft. http://samples.gotdotnet.com/QuickStart/aspplus/default.aspx?url=/quickstart/howto/doc/WinForms/WinFormsThreadMarshalling.aspx
-- Please see the code that is same as I did.

Following this link, the first thing it says is:

"Windows Forms controls can only execute on the thread on which they were created"


This is precisely my point. I'll say it one more time:

The problem is in your ReadSocket function.

The code you have in the ReadSocket function does NOT work in the way recommended by that article.

PLEASE GO AND RE-READ THE CODE IN THE ReadSocket FUNCTION. I know that your code does it correctly in the StartReceive function and the LoadThread function - that code does indeed do the same thing as the article. I'm not complaining about the StartReceive function or the LoadThread function. I'm complaining about the ReadSocket function, which does several things to listView2, despite running on a non-UI thread. The code in the ReadSocket function does NOT follow the recommendations (i.e. it doesn't use Control.Invoke), which is what I'm complaining about.

There is not a single call to Invoke or BeginInvoke inside the ReadSocket method, nor do you use MethodInvoker in there.


I don't know how many more ways I can find to say this. Your code gets it right in two places (StartReceive and LoadThread) and wrong in one place: ReadSocket


As a side issue, I think you have misunderstood why Microsoft's example calls Thread.Sleep(500) after BeginInvoke - it is only doing that to fake up some 'work'. That's in their example because their worker thread doesn't do anything real, so it just sleeps instead. You didn't need to copy that bit across. It doesn't make your code actively wrong as such, but it does mean that it pointlessly wastes some time.

--
Ian Griffiths
DevelopMentor
GeneralRe: Terrible! Pin
titusb#10-Dec-05 8:59
titusb#10-Dec-05 8:59 
GeneralRe: Terrible! Pin
Odis Wooten20-Mar-02 18:15
Odis Wooten20-Mar-02 18:15 
GeneralRe: Building sockets apps (was Terrible!) Pin
Ian Griffiths20-Mar-02 23:31
Ian Griffiths20-Mar-02 23:31 
GeneralRe: Building sockets apps (was Terrible!) Pin
Christian Uhlig7-Apr-04 22:00
Christian Uhlig7-Apr-04 22:00 
GeneralNot a bug Pin
Matthias Mann28-Oct-01 7:18
Matthias Mann28-Oct-01 7:18 
QuestionThread safe? Pin
Nemanja Trifunovic5-Oct-01 8:32
Nemanja Trifunovic5-Oct-01 8:32 
GeneralPart of Source code is missing Pin
5-Oct-01 4:37
suss5-Oct-01 4:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.