博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#网络编程之---TCP协议的同步通信(二)
阅读量:6226 次
发布时间:2019-06-21

本文共 7954 字,大约阅读时间需要 26 分钟。

上一篇学习日记中以服务端接受客户端的请求连接结尾

既然服务端已经与客户端建立了连接,那么沟通通道已经打通,载满数据的小火车就可以彼此传送和接收了。现在让我们来看看数据的传送与接收

先把服务端与客户端的连接代码敲出来

服务端IPAddress ip = new IPAddress(new byte[] { 127, 1, 1, 1 });TcpListener server = new TcpListener(ip, 8005);server.Start();//服务端启动侦听 TcpClient client = server.AcceptTcpClient();//接受发起连接对象的同步方法 Console.WriteLine("收到客户端连接请求")//如果没有客户端请求连接,这句话是无法Print out的
客户端IPAddress ip=IPAddress.Parse("127.1.1.1");TcpClient client=new TcpClient();client.Connect(ip,8005);//8005端口号,必须与服务端给定的端口号一致,否则天堂无门

先看看服务端的特殊标记的那句代码

AcceptTcpClient() 这个方法是一个同步方法,在没有接受到连接请求的时候,位于它下面的代码是不会被执行的,也就是线程阻塞在这里,进行不下去了,想出城没有城防长官的批复是不能的,嘿嘿...

连接后,客户端要发送数据给服务端,先贴代码再说

NetworkStream dataStream=client.GetStream();string msg="服务端亲启!";byte[] buffer=Encoding.default.getBytes(msg);stream.write(buffer,0,buffer.length); //这段代码呈接上面那段客户端代码

NetworkStream 在网络中进行传输的数据流,也就是说传输数据必须写入此流中,才能够互通有无。

首先客户端先获取用于发送信息的流,然后将要发送的信息存入byte[] 数组中(数据必须是byte[] 才能够写入流中),最后就是写入传输的数据流,发送

聪明的你想必已经知道如何在服务端获取数据了

既然客户端费力的把数据包装发给服务端了,那么服务端自然要把包装拆了,得到数据,上代码:

NetworkStream dataStream=client.GetStream();byte[] buffer=new byte[8192];int dataSize=dataStream.Read(buffer,0,8192);Console.write(Encoding.default.GetString(buffer,0,dataSize));//这段代码呈接上面那段服务端代码

代码一写,我觉得再说多余了,不过还要在说一两句,嘿嘿

Read() 方法需要三个参数,1,存储数据的缓存空间。2,写入数据的起始点就是从存储空间的什么位置开始写入数据。3,就是存储空间的大小。返回写入数据的大小值
Encoding.default.GetString() 参数解析
1,存储数据的缓存空间。2,从什么位置开始接收数据。3,接收多少数据

以上只是再简单不过的数据发送,而且只是客户端发给服务端,只能发一条信息而已,那如果想彼此互发,并且想发多少条信息都可以,怎么办呢

首先基于以上的代码,编写一个WPF的小程序

下图分别是客户端和服务端

界面很简单,要实现的功能就是客户端与服务端互发信息。

感觉还是直接上代码吧

服务端的全部代码如下: 

public delegate void showData(string msg);//委托,防止跨线程的访问控件,引起的安全异常        private const int bufferSize = 8000;//缓存空间        private TcpClient client;        private TcpListener server;                ///         /// 结构体:Ip、端口        ///         struct IpAndPort        {            public string Ip;            public string  Port;        }        ///         /// 开始侦听        ///         ///         ///         private void btnStart_Click(object sender, RoutedEventArgs e)        {            if (txtIP.Text.Trim() == string.Empty)            {                return;            }            if (txtPort.Text.Trim() == string.Empty)            {                return;            }            Thread thread = new Thread(reciveAndListener);       //如果线程绑定的方法带有参数的话,那么这个参数的类型必须是object类型,所以讲ip,和端口号 写成一个结构体进行传递            IpAndPort ipHePort = new IpAndPort();            ipHePort.Ip = txtIP.Text;            ipHePort.Port = txtPort.Text;            thread.Start((object)ipHePort);        }              ///         /// 发送信息给客户端        ///         ///         ///         private void btnSend_Click(object sender, RoutedEventArgs e)        {            if (txtSendMsg.Text.Trim() != string.Empty)            {                NetworkStream sendStream = client.GetStream();//获得用于数据传输的流                byte[] buffer = Encoding.Default.GetBytes(txtSendMsg.Text.Trim());//将数据存进缓存中                sendStream.Write(buffer,0,buffer.Length);//最终写入流中                txtSendMsg.Text = string.Empty;            }        }        ///         /// 侦听客户端的连接并接收客户端发送的信息        ///         /// 服务端Ip、侦听端口        private void reciveAndListener(object ipAndPort)        {            IpAndPort ipHePort = (IpAndPort)ipAndPort;            IPAddress ip = IPAddress.Parse(ipHePort.Ip);            server = new TcpListener(ip, int.Parse(ipHePort.Port));            server.Start();//启动监听            rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "服务端开启侦听....\n");          //  btnStart.IsEnabled = false;            //获取连接的客户端对象            client = server.AcceptTcpClient();            rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText),"有客户端请求连接,连接已建立!");//AcceptTcpClient 是同步方法,会阻塞进程,得到连接对象后才会执行这一步              //获得流            NetworkStream reciveStream = client.GetStream();            #region 循环监听客户端发来的信息            do            {                byte[] buffer = new byte[bufferSize];                int msgSize;                try                {                    lock (reciveStream)                    {                        msgSize = reciveStream.Read(buffer, 0, bufferSize);                    }                    if (msgSize == 0)                        return;                    string msg = Encoding.Default.GetString(buffer, 0, bufferSize);                    rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "\n客户端曰:" + Encoding.Default.GetString(buffer, 0, msgSize));                }                catch                {                    rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "\n 出现异常:连接被迫关闭" );                    break;                }            } while (true);            #endregion        }

客户端代码:

TcpClient client;        private const int bufferSize = 8000;        NetworkStream sendStream;        public delegate void showData(string msg);        private void btnConnect_Click(object sender, RoutedEventArgs e)        {            if (txtIP.Text.Trim() == string.Empty)            {                return;            }            if (txtPort.Text.Trim() == string.Empty)            {                return;            }            IPAddress ip = IPAddress.Parse(txtIP.Text);            client = new TcpClient();            client.Connect(ip, int.Parse(txtPort.Text));            rtbtxtShowData.AppendText("开始连接服务端....\n");            rtbtxtShowData.AppendText("已经连接服务端\n");            //获取用于发送数据的传输流            sendStream = client.GetStream();            Thread thread = new Thread(ListenerServer);            thread.Start();        }        private void btnSend_Click(object sender, RoutedEventArgs e)        {            if (client != null)            {                //要发送的信息                if (txtSendMsg.Text.Trim() == string.Empty)                    return;                string msg = txtSendMsg.Text.Trim();                //将信息存入缓存中                byte[] buffer = Encoding.Default.GetBytes(msg);                //lock (sendStream)                //{
sendStream.Write(buffer, 0, buffer.Length); //} rtbtxtShowData.AppendText("发送给服务端的数据:" + msg + "\n"); txtSendMsg.Text = string.Empty; } } private void ListenerServer() { do { try { int readSize; byte[] buffer = new byte[bufferSize]; lock (sendStream) { readSize = sendStream.Read(buffer, 0, bufferSize); } if (readSize == 0) return; rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "服务端曰:" + Encoding.Default.GetString(buffer, 0, readSize)+"\n"); } catch { rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "报错"); } //将缓存中的数据写入传输流 } while (true); }

其中用到了,多线程处理还有委托,因为以上我们用到的不管是Connect,还是AcceptTcpClient方法 都是同步方法,会阻塞进程,导致窗口无法自由移动

rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "服务端开启侦听....\n");

上面这句代码或许有些人不解,我也花了一些时间才懂这样写的

其实由于在WPF中不允许跨线程访问,访问了会抛异常,但是在WPF中的窗口控件都有一个Dispatcher(调度器)属性,允许访问控件的线程;既然不允许直接访问,就告诉控件我们要干什么就好了。

所以在多线程中使用控件的Dispatcher属性,这样就不是跨线程访问了,然后我们在看看Invoke方法

 

通过上面的标示,看的出需要一个委托类型的方法,所以就将RichTextBox 的赋值方法AppendText 绑定到一个委托showData上。

下面是一段引用,看了或许能更明白点

WPF的UI线程都交给一个叫做调度器的类了。     WPF 应用程序启动时具有两个线程:一个用于处理呈现,另一个用于管理 UI。 呈现线程实际上隐藏在后台运行,而 UI 线程则接收输入、处理事件、绘制屏幕以及运行应用程序代码。UI 线程在一个名为 Dispatcher 的对象中将工作项进行排队。 Dispatcher 根据优先级选择工作项,并运行每一个工作项直到完成。Dispatcher 类提供两种注册工作项的方法:Invoke 和 BeginInvoke。 这两个方法都会安排执行一个委托。Invoke 是同步调用,即它直到 UI 线程实际执行完该委托时才返回。BeginInvoke 是异步调用,因而将立即返回。------引用自
 

执行以上程序的效果图:

Ok,至此客户端与服务端的数据传递就大功告成了,这只是一个很简单的操作,如果有多个客户端呢?要求异步通信,怎么办?不急,慢慢来,不积跬步无以至千里

如果有什么错的,希望指正。

转载于:https://www.cnblogs.com/MrALei/p/3582755.html

你可能感兴趣的文章
【转】Lua编程规范
查看>>
P4779 【模板】单源最短路径(标准版)
查看>>
二三维联动之MapControl与SceneControl的联动
查看>>
cocos2dx ScrollView 测试二 自定义Item和boundingBox
查看>>
洛谷P4175 网络管理
查看>>
js监听input输入字符变化
查看>>
tcpdump详解
查看>>
JAVA基础:ArrayList和LinkedList区别
查看>>
不仅仅完成功能,避免无效成本浪费
查看>>
[转载]SCSF 系列:Smart Client Software Factory 中 MVP 模式最佳实践
查看>>
什么是零宽断言
查看>>
复制延迟排查
查看>>
5.01 列出模式中的表
查看>>
Algs4-1.4DoublingRatio
查看>>
html介绍和head标签
查看>>
(转)C语言位运算详解
查看>>
[题解]Codeforces Round #519 - B. Lost Array
查看>>
Python 学习 —— 进阶篇(装饰器、类的特殊方法)
查看>>
Part 6.系统编程之线程--1(创建,全局变量访问,与进程对比)
查看>>
Unicode和UTF-8
查看>>