C#网络编程入门之TCP

C#网络编程入门之TCP

目录:
C#网络编程入门系列包括三篇文章:

(一)C#网络编程入门之UDP

(二)C#网络编程入门之TCP

(三)C#网络编程入门之HTTP

一、概述
UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来p ! J K实现UDP和TCP通讯,由于.NET框架8 I b o通过UdpClient、TcpListener 、TcpClie q Z H Z 3 E 4ent这几个类对Socket进行了封装,使` # )其使用更加方便,本文就通过这几个封装过的类讲解一下相关应用。

二、基本应用:连接、发? ) X j g ! | Q N送、接收
服务端建立侦听并等待连接:

TcpListener tcpListener =7 $ S Y new TcpListener(IPAddress.Parse("127.0.0.1"), 9000);
tcpListen1 Z ^er.Start();
if (tcpListener.Pending())
{

      TcpClient client = tcpListener.AcceptTcpC* = V p c # Z 1liena Y | ; $t();
Console.WriteLine("Connected"Z ;  O o);   

}

服务端是通过AccH N |eptT: _ } 2cpClient方法获得Tc; i 9pClient对象,而客户端是直接创建TcpClient对象。

TcpClient tcpClient = new TcpClient();
tcpClient.Connect("127.0.0.1", 9000);
发送数据Te y } 2cpClient对象创建后,发送接收都通过TcpClient对象完u $ T i )成。

发送数据:

            TcpClient tcpClient = new TcpClientD - ( ) 5 C();
tcpCli$ v 5 gent.0 3 # ~Connect("127.0.0.1", 9000);
NetworkStream netStream = tcpClient.GetStream( R ! ` Z ~ v W G);
int Len = 1024;
byte[] datas = new byte[Len];
netStream.WritN O k ( y 4e(datas, 0, L[ : 1 % L ; Den);% F c Y 6 w u a L
netStream.Close();
tcpClient.Close();

接收数据:

TcpClient client = tcpListener.AcceptTcpClient();
Console.WriteLine("Connec* v ;ted");

NetworkStream stream? ; s ( . = client.GetStream();
var remote = client.Client.RemoteEndPoint;

byte[] data = new by0 I .te[1024];
while (true)
{

     if (stream.DataAvailable)
{
int len = stream.Read(data, 0, 1024);
Console.WriteLine($"From:{remote}:Received ({len})");
}
Thread.Se } ileep(1);

}

三、 粘包问题
和UDP不太一样,TCP连接不会丢包,但存在粘包v K o h 0 t 6 ) )问题。(严格来说粘包这个说法是不严谨的,因为TCP通讯是基于流的,没有包的概念,包只是使用者自己的理解。) 下面分析一下粘包产生的原因及解决办法

TCP数N E |据通讯是基于流来{ ( 4 + ;实现的,类似一个队列,当有数据发送过来时,操作系统就会把发送过来的数据依次放到这个队j F 4 - K ` m _列中,对发送者而言,数据是一片一片发送的,所以自然会认为存在数据包的概念,但对于接收者而言,如果没有及时去取2 p p 2 { a这些数据,这些数据依次存放在队列中,彼此之间并无明显间隔,自9 & J u然就粘包了。

还有一种情况粘包是发送端造成的,有时我们调用$ 3 0 A r c P O R发送代码时,操作系统可能并不会立即发送,而是放到缓存区,当缓存区达到一定数量时才6 4 E真正发送。

要解决粘包问题,大致有以下几个方案。

1、 约定数据长度,发送端的数据都是指定长度,比如1024;接收端取数据时也取同样长度,不i b X t够长度就等待,保证取到的数据和发送端一致;

2、 接收端取数据的频率远大于发送端,比如发送端每1秒B / X i k H L . n发送一段数据,接收端每0.1秒去取一次x 4 3 |数据,这样基本可以保证数据不会粘起来;

以上两个方案都要求发送端需要立即发送,不可缓存数据。而且这两种方案都有缺陷:首先,第一种方案:如果要q W w ; J包大小一致的话,如果约定的包比较大,肯定有较多数据冗余,浪费网络资d ~ Z 2 H (源,如果包较小,连接就& ) M M I E E (比较频繁y 3 ( C (,效率不高。

其次,第二种方案:这v { ` #个方案只能在理想环境下可以实现,当服务端遭遇一段时间的计算压力时可能会出现意外,不能完全保证。

比较完善的解决方案就是对接收到4 F 3 )的数据进行预处理:首先通过定义特殊的字符组合作为包头和包尾,如果传输ASCII字符,R N M可以用0x02表示开始(STn 5 Z e Q 3 J s gX)h , % O h 7,用0x03表示结束(ETX),比如:STX ‘H’ ‘e’ ‘lU Z V l 8 [ x M’ ‘l’ ‘o’ ETX (二进制数据: 02 48 65 6C 6? j e 3 v G MC 6F 03)。如果数据较长可以在包头留出固定位置存放包长度, 如:C K c 9 e : & ]

0200 0548 65 6C 6C 6FQ W r J m I / h 03

其中02 05 就表示正文长度为5个字节,可以进行} c D A Z 6 w m校验^ 8 : N j {

虽然第三种方案比较严谨,但相对复杂,在传输比较可靠、应用比] D T Q [ * Q较简单的场景下,也可以采用前面& 0 ( {两种解决方案& - x { L

四、 一个完整的例程
服务端:

View CodU H { H m q ,e
客户端:

View Code
原文地址https://www.cnblogs.c~ A ~ B g # K ^om/seabluescn/p/12972632.html