Rainyboy 发表于 2010-12-21 13:10

小练习:Tcp通信+线程间异步调用(In C#)

本帖最后由 Rainyboy 于 2010-12-21 13:27 编辑

关于网络编程,现代的编程语言都会涉及,这里给出的是一个小练习,可以实现局域网聊天。


关于异步调用,一个形象的例子在帖子VC中如何实现一个窗口显示几秒后自动关闭?中就被提出过了。Windows下运行的程序都不停地等待用户的指令(即消息),然后在自身的消息-响应映射表中循环,找到合适的处理函数来对这个消息作出响应。因此,如刷新界面,接受用户的键盘输入,显示菜单等等都需要由程序的主线程来完成。这就导致了windows程序的主线程不适宜被长时间的阻塞,在某些时候,这又是不可避免的,例如本例中的TcpListener,就需要建立新的线程来单独负责此事,然而新的线程在执行时有需要对界面进行操作,在本例中,是实时地显示接受到的消息。为了满足这一系列需求,在这个小练习中采用了.NET平台中提供的代理(delegate)技术,代理即C/C++中函数指针的拓展,参考资料多如牛毛,具体用法无须赘述,上例子吧。











流程图为:



代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;


namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
      /// <summary>
      /// 结束线程的标记
      /// </summary>
      public bool IsGoOn = true;

      /// <summary>
      /// 构造代理
      /// </summary>
      public delegate void Show_Rec_Delegate(String Msg);
      public delegate void Show_Stp_Delegate(int Ds);
      public delegate void Show_Sys_Delegate(String Msg);

      /// <summary>
      /// 通信层变量
      /// </summary>
      TcpListener server = null;

      public Form1()
      {
            InitializeComponent();
      }

      /// <summary>
      /// 开始发送内容
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void buttonOK_Click(object sender, EventArgs e)
      {
            try
            {
                TcpClient client = new TcpClient(Str_TargetIP.Text, Convert.ToInt16(Str_SendPort.Text));
                String message = UserName.Text +": " +MsgSend.Text;
                Byte[] data = System.Text.Encoding.Unicode.GetBytes(message);
                NetworkStream stream = client.GetStream();
                stream.Write(data, 0, data.Length);
                WriteSysMsg("Sent: " + message);
                WriteRecMsg(message);
                stream.Close();
                client.Close();
                MsgSend.Text = String.Empty;
                toolStripProgressBar1.Value += 1;
            }
            catch (ArgumentNullException ex)
            {
                WriteSysMsg("ArgumentNullException: "+ ex.Message);
            }
            catch (SocketException ex)
            {
                WriteSysMsg("SocketException: "+ ex.Message);
            }
      }

      /// <summary>
      /// 创建并启动监听线程
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Form1_Load(object sender, EventArgs e)
      {
            System.Net.IPAddress addr;

            // 获得本机局域网IP地址

            addr = new System.Net.IPAddress(Dns.GetHostByName(Dns.GetHostName()).AddressList.Address);

            LocalIP.Text = addr.ToString();
      }

      /// <summary>
      /// 用以将Msg显示在接收区域
      /// </summary>
      /// <param name="Msg"></param>
      public void WriteRecMsg(String Msg)
      {
            MsgRecieve.Text += System.DateTime.Now+ Environment.NewLine + "" + Msg + Environment.NewLine;
      }

      /// <summary>
      /// 用以将Msg显示在系统消息区域
      /// </summary>
      /// <param name="Msg"></param>
      public void WriteSysMsg(String Msg)
      {
            MsgSys.Text = System.DateTime.Now + ":" + Environment.NewLine + "" + Msg + Environment.NewLine + MsgSys.Text;
      }

      /// <summary>
      /// 用以用进度条记录当前监听线程的进度
      /// </summary>
      public void WriteListenStep(int Ds)
      {
            toolStripProgressBar2.Value = (toolStripProgressBar1.Value + Ds) % 100;
      }


      /// <summary>
      /// UDP监听线程的实际操作函数
      /// </summary>
      public void UDP_Listener()
      {
            try
            {
                //实例化代理
                Show_Rec_Delegate Show_Rec = new Show_Rec_Delegate(this.WriteRecMsg);
                Show_Stp_Delegate Show_Stp = new Show_Stp_Delegate(this.WriteListenStep);
                Show_Sys_Delegate Show_Sys = new Show_Sys_Delegate(this.WriteSysMsg);

                this.Invoke(Show_Sys, new object[] { "Tcp Listener Thread Start!" });
                try
                {
                  Int32 port = Convert.ToInt32(Str_LinstenPort.Text);
                  IPAddress localAddr = IPAddress.Parse(LocalIP.Text);
                  server = new TcpListener(localAddr, port);
                  server.Start();
                  Byte[] bytes = new Byte;
                  String data = null;
                  // 进入监听循环
                  while (IsGoOn)
                  {
                        this.Invoke(Show_Sys, new object[] { "Waiting for a connection... " });
                        TcpClient client = server.AcceptTcpClient();
                        this.Invoke(Show_Sys, new object[] { "Connected!" });
                        data = null;
                        NetworkStream stream = client.GetStream();
                        int i;
                        while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                        {
                            data = System.Text.Encoding.Unicode.GetString(bytes, 0, i);
                            this.Invoke(Show_Sys, new object[] { "Received: " + data });
                            this.Invoke(Show_Rec, new object[] { data });
                        }
                        client.Close();
                        //记录进度
                        this.Invoke(Show_Stp, new object[] { 1 });
                        Thread.Sleep(100);
                  }
                }
                catch (SocketException ex)
                {
                  this.Invoke(Show_Sys, new object[] { "SocketException: " + ex.Message });
                }
                finally
                {
                  //退出server
                  server.Stop();
                  this.Invoke(Show_Sys, new object[] { "Tcp Listener Thread End!" });
                }
            }
            catch
            {
                ;
            }
      }

      /// <summary>
      /// 在关闭窗口时结束线程
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Form1_FormClosing(object sender, FormClosingEventArgs e)
      {
            IsGoOn=false;
            if(server!=null)
            {
                server.Stop();
            }
      }

      private void 开始监听ToolStripMenuItem_Click(object sender, EventArgs e)
      {
            Thread UDP_lis_th = new Thread(new ThreadStart(this.UDP_Listener));
            UDP_lis_th.Start();
            toolStripProgressBar1.Value = 10;
            toolStripProgressBar2.Value = 10;
            //锁定参数窗口
            Str_LinstenPort.Enabled = false;
            Str_SendPort.Enabled = false;
            Str_TargetIP.Enabled = false;
            UserName.Enabled = false;
            LocalIP.Enabled = false;
      }

      private void 结束监听ToolStripMenuItem_Click(object sender, EventArgs e)
      {
            IsGoOn = false;
            if (server != null)
            {
                server.Stop();
            }
            WriteSysMsg("Tcp Listener Thread End!");
            toolStripProgressBar1.Value = 10;
            toolStripProgressBar2.Value = 10;
            Str_LinstenPort.Enabled = true;
            Str_SendPort.Enabled = true;
            Str_TargetIP.Enabled = true;
            UserName.Enabled = true;
            LocalIP.Enabled = true;
      }

      private void buttonExit_Click(object sender, EventArgs e)
      {
            IsGoOn = false;
            if (server != null)
            {
                server.Stop();
            }
            Close();
      }

    }
}

如何使用:


填好相应的内容后,请点击“开始监听”


附件为编译后的程序,运行需安装.NET framwork。



fxuestc 发表于 2010-12-23 10:24

你太牛了,,我搞通信的都不会。。。。。。{:{28}:}{:{28}:}

firecat_2 发表于 2010-12-23 10:27

代码规范 示例很好

Rainyboy 发表于 2010-12-23 10:57

回复 2 # fxuestc 的帖子

哪里,你会起来很容易的,正是你们搞通信的所作出的工作,我们才能这么轻松地,在不了解底层细节的情况下,编写应用层的软件。写这个程序的时候才强烈的感受到,什么叫“分层”,什么叫“协议”,以及这些东西为什么能给软件开发带来便利。看看你们搞通信的,做得多么好,教科书般的经典!

fxuestc 发表于 2010-12-23 18:51

回复 4 # Rainyboy 的帖子

你太让我汗颜了{:{28}:}{:{28}:}{:{28}:}

qyue10600 发表于 2011-2-16 11:51

先下下来了。。。学习{:{23}:}

dengz 发表于 2011-3-2 20:51

长见识啦,{:{23}:}

zhangjingtxwd 发表于 2011-6-15 09:18

很好啊,很厉害啊

zyjfrank 发表于 2012-3-15 22:55

先下了学习 谢谢楼主{:{39}:}

rencs1 发表于 2012-7-16 20:03

{:{39}:}学习了,谢谢斑竹。。。

hllove4ever 发表于 2016-7-11 00:23

真是好东东,对楼主的辛勤工作和无私奉献致以崇高的敬意。

Agoni 发表于 2016-7-11 08:28

连流程都有good
页: [1]
查看完整版本: 小练习:Tcp通信+线程间异步调用(In C#)