上一篇学习日记C#网络编程之--TCP协议(一)中以服务端接受客户端的请求连接结尾
既然服务端已经与客户端建立了连接,那么沟通通道已经打通,载满数据的小火车就可以彼此传送和接收了。现在让我们来看看数据的传送与接收

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

服务端
IPAddress ip = , , ,  });
TcpListener server = );
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端口号,必须与服务端给定的端口号一致,否则天堂无门

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

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

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

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

NetworkStream 在网络中进行传输的数据流,也就是说传输数据必须写入此流中,才能够互通有无。
首先客户端先获取用于发送信息的流,然后将要发送的信息存入byte[] 数组中(数据必须是byte[] 才能够写入流中),最后就是写入传输的数据流,发送

聪明的你想必已经知道如何在服务端获取数据了
既然客户端费力的把数据包装发给服务端了,那么服务端自然要把包装拆了,得到数据,上代码:

NetworkStream dataStream=client.GetStream();
];
,);
Console.write(Encoding.,dataSize));
//这段代码呈接上面那段服务端代码

代码一写,我觉得再说多余了,不过还要在说一两句,嘿嘿
Read() 方法需要三个参数,1,存储数据的缓存空间。2,写入数据的起始点就是从存储空间的什么位置开始写入数据。3,就是存储空间的大小。返回写入数据的大小值
Encoding.default.GetString() 参数解析
1,存储数据的缓存空间。2,从什么位置开始接收数据。3,接收多少数据

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

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

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

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

感觉还是直接上代码吧

服务端的全部代码如下:

        public delegate void showData(string msg);//委托,防止跨线程的访问控件,引起的安全异常
        ;//缓存空间
        private TcpClient client;
        private TcpListener server;

        /// <summary>
        /// 结构体:Ip、端口
        /// </summary>
        struct IpAndPort
        {
            public string Ip;
            public string  Port;
        }

        /// <summary>
        /// 开始侦听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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);
        }      

        /// <summary>
        /// 发送信息给客户端
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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,,buffer.Length);//最终写入流中
                txtSendMsg.Text = string.Empty;
            }

        }

        /// <summary>
        /// 侦听客户端的连接并接收客户端发送的信息
        /// </summary>
        /// <param name="ipAndPort">服务端Ip、侦听端口</param>
        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, , bufferSize);
                    }
                    )
                        return;
                    , bufferSize);
                    rtbtxtShowData.Dispatcher.Invoke(, msgSize));
                }
                catch
                {
                    rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "\n 出现异常:连接被迫关闭" );
                    break;
                }
            } while (true);

            #endregion
        }

客户端代码:

       TcpClient client;
        ;
        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, , 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, , bufferSize);
                    }
                    )
                        return;
                    rtbtxtShowData.Dispatcher.Invoke(, 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 是异步调用,因而将立即返回。------引用自WPF笔记12: 线程处理模型

执行以上程序的效果图:

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

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

C#网络编程之---TCP协议的同步通信(二)的更多相关文章

  1. 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

    Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...

  2. C#网络编程之--TCP协议(一)

    TCP 是面向连接的传输协议 面向连接,其实就好比,A打电话给B,如果B接听了,那么A和B之间就的通话,就是面向连接的 TCP 是全双工的传输协议 全双工,这个理解起来也很简单,A打电话给B,B接听电 ...

  3. C# socket网络编程 基于TCP协议

    socket 服务器端: 1.创建socket Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ...

  4. 嵌入式linux的网络编程(1)--TCP/IP协议概述

    嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...

  5. java 网络编程 UDP TCP

    网络编程 网络编程主要用于解决计算机与计算机(手机.平板..)之间的数据传输问题. 网络编程: 不需要基于html页面就可以达到数据之间的传输. 比如: feiQ , QQ , 微信....网页编程: ...

  6. UNIX网络编程——解决TCP网络传输“粘包”问题

    当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API).TCP/IP传输层有两个并列的协议:TCP和UDP.其中TCP(transport contro ...

  7. JAVA基础知识之网络编程——-基于UDP协议的通信例子

    UDP是一种不可靠的协议,它在通信两端各建立一个socket,这两个socket不会建立持久的通信连接,只会单方面向对方发送数据,不检查发送结果. java中基于UDP协议的通信使用DatagramS ...

  8. JAVA基础知识之网络编程——-基于TCP通信的简单聊天室

    下面将基于TCP协议用JAVA写一个非常简单的聊天室程序, 聊天室具有以下功能, 在服务器端,可以接受客户端注册(用户名),可以显示注册成功的账户 在客户端,可以注册一个账号,并用这个账号发送信息 发 ...

  9. iOS UI高级之网络编程(HTTP协议)

    HTTP协议的概念 HTTP协议,Hyper Text Transfer Protocol (超文本传输协议)是用于从万维网服务器传送超文本到本地浏览器的传输协议,HTTP是一个应用层协议,由请求和响 ...

随机推荐

  1. KeySpaceNotification 键空间通知

    KeySpaceNotification 键空间通知 1.Redis键淘汰机制简介 在Redis中,内存的大小是有限的,所以为了防止内存饱和,需要实现某种键淘汰策略.主要有两种方法,一种是当Redis ...

  2. ReactNative新手学习之路05 使用夜神模拟器调试ReactNative

    1.首先确保adb环境添加到path环境   2.安装好夜神模拟器   3.运行模拟器   4.adb connect 127.0.0.1:62001   5.摇一摇设置IP和端口 如127.168. ...

  3. Q的深层嵌套

    1.如果将异步方法用同步的方式执行,try catch能捕获到错误,同时不会阻塞到主进程,因此console.log(3333)能执行. var fs = require('fs'); try { v ...

  4. 模板方法模式(Template Method Pattern)

    模板方法模式是一种基于继承的代码复用技术,定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. 模式中的角色 抽象类(Abstrac ...

  5. php中cookie技术关于跨目录调用cookie值的问题

    今天做项目发现了一个奇葩错误,以cookie技术为主,反复测试发现cookie不能跨目录调用. 我在F:wamp\www\test\下面有1.php和2.php其中1.php接受2.php中setco ...

  6. mysql安装中出现的问题,

    花了一天的时间明天mysql的安装方法: 自己的错误: 主要原因: (1):bin文件坏境配置出现了问题,没有重新在系统中配置文件 解决方法:右击电脑——属性——高级系统设置——变量配置——在path ...

  7. 基于DDD的.NET开发框架 - ABP日志Logger集成

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  8. Sql Server 索引之唯一索引和筛选索引

    唯一索引(UNIQUE  INDEX) 当主键创建时如果不设置为聚集索引,那么就一定是唯一的非聚集索引.实际上,唯一索引,故名思议就是它要求该列上的值是唯一的.唯一索引能够保证索引键中不包含重复的值, ...

  9. 51nod算法马拉松14

    这次太丢人了只搞出来了A到D,那就将就写一写A到D... A 棋盘问题 脑筋急转弯题,不难发现每一次两个人只能染白奇数个格子,所以数数有奇数还是偶数个白格子就行了. #include<cstdi ...

  10. 【C】 04 - 表达式和语句

    程序的生命力体现在它千变万化的行为,而再复杂的系统都是由最基本的语句组成的.C语句形式简单自由,但功能强大.从规范的角度学习C语法,一切显得简单而透彻,无需困扰于各种奇怪的语法. 1. 表达式(exp ...