您的位置 首页 > 娱乐休闲

C TCP网络编程传递数据方法及加密

TCP 是面向连接的传输协议

面向连接,其实就好比,A打电话给B,如果B接听了,那么A和B之间就的通话,就是面向连接的

TCP 是全双工的传输协议

全双工,这个理解起来也很简单,A打电话给B,B接听电话,那么A可以说话给B听,同样B也可以给A说话,不可能只允许一个人说话.

TCP 是点对点的

点对点,这个看了上面的举例相比大家都知道了,还要说一点的是,如果在A和B打电话过程中,B又来了一个紧急电话,那么B就要将与A的通话进行通话保持,所以不管怎么讲同一个连接只能是点对点的,不能一对多。

TCP 是可靠地数据传输

可靠地,一旦建立了连接,数据的发送一定能够到达,并且如果A说“你好吗?” B不会听到“吗你好”,这就是可靠地数据传输

说完了TCP的诸多优点,再说说TCP的另一个重要的合作伙伴:套接字(Socket)

好吧,说实话我不知道如何形象的说套接字,或许我不太理解的缘故,粗略的说一下吧,TCP是传输层的协议,用于数据传送的,在我们编写短信时,短信窗口其实就是一个应用程序,当我们发送短信时,短信从应用程序滚到传输通道,应用程序和传输通道间就存在一个套接字的东西,没有套接字,应用程序就无法将短信滚到传输通道。额...感觉灰常别扭。

如果想清楚的了解套接字是什么 请大家移步 C#网络编程

套接字包含的最重要的也就是两个信息:连接至远程的本地的端口信息(本机地址和端口号),连接到的远程的端口信息(远程地址和端口号)。 .NET提供了两个类将对套接字的编程进行了一个封装,这两个类是TcpClient和TcpListener, TcpListener用于接受连接请求,而TcpClient则用于接收和发送流数据。TcpListener持续地保持对端口的侦听,一旦收到一个连接请求后,就可以获得一个TcpClient对象,而对于数据的发送和接收都有TcpClient去完成。此时,TcpListener并没有停止工作,它始终持续地保持对端口的侦听状态。----张子阳《C#网络编程(基本概念和操作) - Part.1》

引用一段话作为过渡

得益于.Net 对套接字进行的封装,我们可以很简单的写一下简单的基础例子,关于TCP协议,通常将发起请求连接的一方为客户端,另一端为服务端

简单服务端代码:

IPAddress ip = IPAddre("127.1.1.1");//服务端地址

其实服务端就像是某机场,某机场的地址就是服务端地址,机场的某条降落跑道就是服务端监听的端口号

客户端代码:

IPAddress ip = IPAddre("127.1.1.1");

看了以上代码,就很明白是怎么回事了,飞机要飞往某个机场肯定要先知道某机场的地址,和机场允许该飞机降落的跑道号,一且都知道了,那么就飞过去,降落吧,嘿嘿

有了以上代码,客户端就可以连接服务端了,但是服务端是如何知道已经于一个客户端连接了呢?

在服务器端开始侦听以后,可以在TcpListener实例上调用AcceptTcpClient()来获取与一个客户端的连接,它返回一个TcpClient类型实例

TcpClient client = ();//server 就是TcpListener的实例

TcpListener 就像一个酒店的前台,可以接受很多对象的连接,职责很单一,就是接受连接请求,将连接请求对象交给TcpClient,然后继续最自己接待引导的工作。

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

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

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

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

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

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

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

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

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

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

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

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

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

Encoding.de() 参数解析

1,存储数据的缓存空间。2,从什么位置开始接收数据。3,接收多少数据

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

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

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

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

感觉还是直接上代码吧

服务端的全部代码如下:

public delegate void showData(string msg);//委托,防止跨线程的访问控件,引起的安全异常private const int bufferSize = 8000;//缓存空间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 () == ) { return; } if () == ) { return; } Thread thread = new Thread(reciveAndListener);//如果线程绑定的方法带有参数的话,那么这个参数的类型必须是object类型,所以讲ip,和端口号 写成一个结构体进行传递 IpAndPort ipHePort = new IpAndPort(); i = ; i = ; ((object)ipHePort); } /// <summary>/// 发送信息给客户端 /// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSend_Click(object sender, RoutedEventArgs e) { if () != ) { NetworkStream sendStream = client.GetStream();//获得用于数据传输的流 byte[] buffer = Encoding.De());//将数据存进缓存中 (buffer,0,bu);//最终写入流中 = ; } } /// <summary>/// 侦听客户端的连接并接收客户端发送的信息 /// </summary>/// <param name="ipAndPort">服务端Ip、侦听端口</param>private void reciveAndListener(object ipAndPort) { IpAndPort ipHePort = (IpAndPort)ipAndPort; IPAddress ip = IPAddre(i); server = new TcpListener(ip, int.Parse(i)); ();//启动监听 r(new showData), "服务端开启侦听....\n"); // b = false; //获取连接的客户端对象client = (); r(new showData),"有客户端请求连接,连接已建立!");//AcceptTcpClient 是同步方法,会阻塞进程,得到连接对象后才会执行这一步 //获得流NetworkStream reciveStream = client.GetStream(); #region 循环监听客户端发来的信息 do { byte[] buffer = new byte[bufferSize]; int msgSize; try { lock (reciveStream) { msgSize = reciveS(buffer, 0, bufferSize); } if (msgSize == 0) return; string msg = Encoding.De(buffer, 0, bufferSize); r(new showData), "\n客户端曰:" + Encoding.De(buffer, 0, msgSize)); } catch { r(new showData), "\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 () == ) { return; } if () == ) { return; } IPAddress ip = IPAddre(); client = new TcpClient(); client.Connect(ip, int.Parse()); r("开始连接服务端....\n"); r("已经连接服务端\n"); //获取用于发送数据的传输流 sendStream = client.GetStream(); Thread thread = new Thread(ListenerServer); (); } private void btnSend_Click(object sender, RoutedEventArgs e) { if (client != null) { //要发送的信息 if () == ) { return; } string msg = .Trim(); //将信息存入缓存中 byte[] buffer = Encoding.De(msg); // lock (sendStream) // { (buffer, 0, bu); // } r("发送给服务端的数据:" + msg + "\n"); = ; } } private void ListenerServer() { do { try { int readSize; byte[] buffer = new byte[bufferSize]; lock (sendStream) { readSize = sendS(buffer, 0, bufferSize); } if (readSize == 0) { return; } r(new showData), "服务端曰:" + Encoding.De(buffer, 0, readSize)+"\n"); } catch { r(new showData), "报错"); } //将缓存中的数据写入传输流 } while (true); }

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

r(new showData), "服务端开启侦听....\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,至此客户端与服务端的数据传递就大功告成了,这只是一个很简单的操作,如果有多个客户端呢?要求异步通信

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“如何跨线程调用windows窗体控件,c#如果跨线程调用windows窗体控件,windows如何查看线程”边界阅读