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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

感觉还是直接上代码吧

服务端的全部代码如下:

  1. public delegate void showData(string msg);//委托,防止跨线程的访问控件,引起的安全异常
  2. private const int bufferSize = ;//缓存空间
  3. private TcpClient client;
  4. private TcpListener server;
  5.  
  6. /// <summary>
  7. /// 结构体:Ip、端口
  8. /// </summary>
  9. struct IpAndPort
  10. {
  11. public string Ip;
  12. public string Port;
  13. }
  14.  
  15. /// <summary>
  16. /// 开始侦听
  17. /// </summary>
  18. /// <param name="sender"></param>
  19. /// <param name="e"></param>
  20. private void btnStart_Click(object sender, RoutedEventArgs e)
  21. {
  22. if (txtIP.Text.Trim() == string.Empty)
  23. {
  24. return;
  25. }
  26. if (txtPort.Text.Trim() == string.Empty)
  27. {
  28. return;
  29. }
  30.  
  31. Thread thread = new Thread(reciveAndListener);
  32.        //如果线程绑定的方法带有参数的话,那么这个参数的类型必须是object类型,所以讲ip,和端口号 写成一个结构体进行传递
  33. IpAndPort ipHePort = new IpAndPort();
  34. ipHePort.Ip = txtIP.Text;
  35. ipHePort.Port = txtPort.Text;
  36.  
  37. thread.Start((object)ipHePort);
  38. }
  39.  
  40. /// <summary>
  41. /// 发送信息给客户端
  42. /// </summary>
  43. /// <param name="sender"></param>
  44. /// <param name="e"></param>
  45. private void btnSend_Click(object sender, RoutedEventArgs e)
  46. {
  47. if (txtSendMsg.Text.Trim() != string.Empty)
  48. {
  49. NetworkStream sendStream = client.GetStream();//获得用于数据传输的流
  50. byte[] buffer = Encoding.Default.GetBytes(txtSendMsg.Text.Trim());//将数据存进缓存中
  51. sendStream.Write(buffer,,buffer.Length);//最终写入流中
  52. txtSendMsg.Text = string.Empty;
  53. }
  54.  
  55. }
  56.  
  57. /// <summary>
  58. /// 侦听客户端的连接并接收客户端发送的信息
  59. /// </summary>
  60. /// <param name="ipAndPort">服务端Ip、侦听端口</param>
  61. private void reciveAndListener(object ipAndPort)
  62. {
  63. IpAndPort ipHePort = (IpAndPort)ipAndPort;
  64.  
  65. IPAddress ip = IPAddress.Parse(ipHePort.Ip);
  66. server = new TcpListener(ip, int.Parse(ipHePort.Port));
  67. server.Start();//启动监听
  68. rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "服务端开启侦听....\n");
  69. // btnStart.IsEnabled = false;
  70.  
  71. //获取连接的客户端对象
  72. client = server.AcceptTcpClient();
  73. rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText),"有客户端请求连接,连接已建立!");//AcceptTcpClient 是同步方法,会阻塞进程,得到连接对象后才会执行这一步
  74.  
  75. //获得流
  76. NetworkStream reciveStream = client.GetStream();
  77.  
  78. #region 循环监听客户端发来的信息
  79.  
  80. do
  81. {
  82. byte[] buffer = new byte[bufferSize];
  83. int msgSize;
  84. try
  85. {
  86. lock (reciveStream)
  87. {
  88. msgSize = reciveStream.Read(buffer, , bufferSize);
  89. }
  90. if (msgSize == )
  91. return;
  92. string msg = Encoding.Default.GetString(buffer, , bufferSize);
  93. rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "\n客户端曰:" + Encoding.Default.GetString(buffer, , msgSize));
  94. }
  95. catch
  96. {
  97. rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "\n 出现异常:连接被迫关闭" );
  98. break;
  99. }
  100. } while (true);
  101.  
  102. #endregion
  103. }

客户端代码:

  1. TcpClient client;
  2. private const int bufferSize = ;
  3. NetworkStream sendStream;
  4. public delegate void showData(string msg);
  5.  
  6. private void btnConnect_Click(object sender, RoutedEventArgs e)
  7. {
  8. if (txtIP.Text.Trim() == string.Empty)
  9. {
  10. return;
  11. }
  12. if (txtPort.Text.Trim() == string.Empty)
  13. {
  14. return;
  15. }
  16. IPAddress ip = IPAddress.Parse(txtIP.Text);
  17. client = new TcpClient();
  18. client.Connect(ip, int.Parse(txtPort.Text));
  19. rtbtxtShowData.AppendText("开始连接服务端....\n");
  20. rtbtxtShowData.AppendText("已经连接服务端\n");
  21. //获取用于发送数据的传输流
  22. sendStream = client.GetStream();
  23. Thread thread = new Thread(ListenerServer);
  24. thread.Start();
  25. }
  26.  
  27. private void btnSend_Click(object sender, RoutedEventArgs e)
  28. {
  29. if (client != null)
  30. {
  31. //要发送的信息
  32. if (txtSendMsg.Text.Trim() == string.Empty)
  33. return;
  34. string msg = txtSendMsg.Text.Trim();
  35. //将信息存入缓存中
  36. byte[] buffer = Encoding.Default.GetBytes(msg);
  37. //lock (sendStream)
  38. //{
  39. sendStream.Write(buffer, , buffer.Length);
  40. //}
  41. rtbtxtShowData.AppendText("发送给服务端的数据:" + msg + "\n");
  42. txtSendMsg.Text = string.Empty;
  43. }
  44. }
  45.  
  46. private void ListenerServer()
  47. {
  48. do
  49. {
  50. try
  51. {
  52. int readSize;
  53. byte[] buffer = new byte[bufferSize];
  54. lock (sendStream)
  55. {
  56. readSize = sendStream.Read(buffer, , bufferSize);
  57. }
  58. if (readSize == )
  59. return;
  60. rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "服务端曰:" + Encoding.Default.GetString(buffer, , readSize)+"\n");
  61.  
  62. }
  63. catch
  64. {
  65. rtbtxtShowData.Dispatcher.Invoke(new showData(rtbtxtShowData.AppendText), "报错");
  66. }
  67. //将缓存中的数据写入传输流
  68. } while (true);
  69. }

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

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

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

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

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

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

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

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

执行以上程序的效果图:

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

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

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

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

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

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

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

  3. 网络编程--使用TCP协议发送接收数据

    package com.zhangxueliang.tcp; import java.io.IOException; import java.io.OutputStream; import java. ...

  4. 网络编程: 基于TCP协议的socket, 实现一对一, 一对多通信

    TCP协议  面向连接 可靠的 面向字节流形式的 tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 TCP协议编码流程: 服务器端:                 客户端 实例化对 ...

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

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

  6. Java学习笔记53(网络编程:TCP协议案例)

    简易的案例 客户端: package demo; import java.io.IOException; import java.io.InputStream; import java.io.Outp ...

  7. 【网络编程1】网络编程基础-TCP、UDP编程

    网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...

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

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

  9. 【转载】[基础知识]【网络编程】TCP/IP

    转自http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=27043 [基础知识][网络编程]TCP/IP iooops  胖友们楼主我又 ...

随机推荐

  1. php实战开发之自我整理(学习笔记)

    PHP没有创建变量的命令,变量会在首次赋值时进行创建. 简单样例 1 <?php $word="My first choice"; $x=5; echo $x; echo & ...

  2. python3 黑板客爬虫闯关游戏(二)

    第二关猜登录密码,需要用到urllib.request和urllib.parse 也很简单,给代码 import urllib.request as ur import urllib.parse as ...

  3. 【MySQL】 empty table and delete table.

    1.MySQL生成删除满足条件的表的sql: 1 SELECT 2 CONCAT( 3 'DROP TABLE ', 4 GROUP_CONCAT(table_name), 5 ';' 6 ) AS ...

  4. Java实现JDBC连接数据库实例

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sq ...

  5. winrt简单克隆对象

    public MapPoint Copy()//MapPoint克隆方法 { MapPoint p = new MapPoint();//这是我自定义的对象 //利用反射获得类成员 FieldInfo ...

  6. execl表格VLOOKUP函数的使用

    使用场景:最近在处理一个表格数据的时候出现了一点麻烦.想把另外表2里面对应的的数据放到表1里面,本来表2的ID是乱序的(这里为好看,就顺序排了.),一个个查找复制粘贴比较麻烦,后经大师指点VLOOKU ...

  7. Redis的缓存策略和主键失效机制

    作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略. >>EXPIRE主键失效机制 在Redis当中,有生存期的key被称为volatile,在创建缓存时,要为给定的key设置 ...

  8. 【基础知识】UML基础

    http://www.ibm.com/developerworks/cn/rational/r-uml/

  9. SQL Server Database 维护计划创建完整的备份策略

    SQL Server的维护计划Maintenance Plan是一个非常实用的维护工具,可以完成大多数的数据库维护任务,通过对这些功能的封装,可以省去很多的代码编写时间.网络上完整介绍的不是很多,特此 ...

  10. 2.Java异常学习

    1.Java异常的概念 异常的例子 1.除法就是一个需要捕获异常的例子,除数又可能是0 异常处理的基本流程如下 一旦发生异常,就使得程序不按照原来的流程继续的运行下去 a.程序抛出异常 try{ th ...