c# 网络编程之tcp

一、概述

UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.NET框架通过UdpClient、TcpListener 、TcpClient这几个类对Socket进行了封装,使其使用更加方便, 本文就通过这几个封装过的类讲解一下相关应用。

二、基本应用:连接、发送、接收

服务端建立侦听并等待连接:

TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 9000);
tcpListener.Start();
if (tcpListener.Pending())
{
   TcpClient client = tcpListener.AcceptTcpClient();
   Console.WriteLine("Connected");
}

服务端是通过AcceptTcpClient方法获得TcpClient对象,而客户端是直接创建TcpClient对象。

TcpClient tcpClient = new TcpClient();
tcpClient.Connect("127.0.0.1", 9000);

发送数据TcpClient对象创建后,发送接收都通过TcpClient对象完成。

发送数据:

TcpClient tcpClient = new TcpClient();
    tcpClient.Connect("127.0.0.1", 9000);
    NetworkStream netStream = tcpClient.GetStream();

    int Len = 1024;
    byte[] datas = new byte[Len];

    netStream.Write(datas, 0, Len);    

    netStream.Close();
    tcpClient.Close();

接收数据:

TcpClient client = tcpListener.AcceptTcpClient();
Console.WriteLine("Connected"); 

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

 byte[] data = new byte[1024];
 while (true)
 {
   if (stream.DataAvailable)
   {
    int len = stream.Read(data, 0, 1024);
    Console.WriteLine($"From:{remote}:Received ({len})");
   }
  Thread.Sleep(1);
 }

三、 粘包问题

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

TCP数据通讯是基于流来实现的,类似一个队列,当有数据发送过来时,操作系统就会把发送过来的数据依次放到这个队列中,对发送者而言,数据是一片一片发送的,所以自然会认为存在数据包的概念,但对于接收者而言,如果没有及时去取这些数据,这些数据依次存放在队列中,彼此之间并无明显间隔,自然就粘包了。

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

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

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

2、 接收端取数据的频率远大于发送端,比如发送端每1秒发送一段数据,接收端每0.1秒去取一次数据,这样基本可以保证数据不会粘起来;

以上两个方案都要求发送端需要立即发送,不可缓存数据。而且这两种方案都有缺陷:首先,第一种方案:如果要包大小一致的话,如果约定的包比较大,肯定有较多数据冗余,浪费网络资源,如果包较小,连接就比较频繁,效率不高。

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

比较完善的解决方案就是对接收到的数据进行预处理:首先通过定义特殊的字符组合作为包头和包尾,如果传输ASCII字符,可以用0x02表示开始(STX),用0x03表示结束(ETX),比如:STX ‘H' ‘e' ‘l' ‘l' ‘o' ETX (二进制数据: 02 48 65 6C 6C 6F 03)。如果数据较长可以在包头留出固定位置存放包长度, 如:

02 00 05 48 65 6C 6C 6F 03

其中02 05 就表示正文长度为5个字节,可以进行校验。

虽然第三种方案比较严谨,但相对复杂,在传输比较可靠、应用比较简单的场景下,也可以采用前面两种解决方案。

四、 一个完整的例程

服务端:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TCPServer
{
 class Program
 {
  static void Main(string[] args)
  {
   TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 9000);
   tcpListener.Start();

   while (true)
   {
    if (tcpListener.Pending())
    {
     TcpClient client = tcpListener.AcceptTcpClient();
     Console.WriteLine("Connected");     

     Task.Run(() =>
     {
      NetworkStream stream = client.GetStream();
      var remote = client.Client.RemoteEndPoint;

      while (true)
      {
       if (stream.DataAvailable)
       {
        byte[] data = new byte[1024];
        int len = stream.Read(data, 0, 1024);
        string Name = Encoding.UTF8.GetString(data,0,len);
        var senddata = Encoding.UTF8.GetBytes("Hello:" + Name);
        stream.Write(senddata, 0, senddata.Length);
       }

       if (!client.IsOnline())
       {
        Console.WriteLine("Connect Closed.");
        break;
       }

       Thread.Sleep(1);
      }
     });
    }

    Thread.Sleep(1);
   }
  }
 }

 public static class TcpClientEx
 {
  public static bool IsOnline(this TcpClient client)
  {
   return !((client.Client.Poll(15000, SelectMode.SelectRead) && (client.Client.Available == 0)) || !client.Client.Connected);
  }
 }
}

客户端:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TCP_Clent
{
 class Program
 {
  static void Main(string[] args)
  {
   ThreadPool.SetMinThreads(100, 100);
   ThreadPool.SetMaxThreads(200, 200); 

   Parallel.For(1, 10, x =>
   {
    SendData("Tom");
   });

   Console.WriteLine("All Completed!");
   Console.ReadKey();
  }

  private static void SendData(string Name)
  {
   Task.Run(() =>
   {
    Console.WriteLine("Start");
    TcpClient tcpClient = new TcpClient();
    tcpClient.Connect("127.0.0.1", 9000);
    Console.WriteLine("Connected");
    NetworkStream netStream = tcpClient.GetStream();

    Task.Run(() =>
    {
     Thread.Sleep(100);
     while (true)
     {
      if (!tcpClient.Client.Connected)
      {
       break;
      }

      if (netStream == null)
      {
       break;
      }

      try
      {
       if (netStream.DataAvailable)
       {
        byte[] data = new byte[1024];
        int len = netStream.Read(data, 0, 1024);
        var message = Encoding.UTF8.GetString(data, 0, len);
        Console.WriteLine(message);
       }
      }
      catch
      {
       break;
      }

      Thread.Sleep(10);
     }
    });

    for (int i = 0; i < 100; i++)
    {
     byte[] datas = Encoding.UTF8.GetBytes(Name);
     int Len = datas.Length;
     netStream.Write(datas, 0, Len);
     Thread.Sleep(1000);
    }

    netStream.Close();
    netStream = null;
    tcpClient.Close();

    Console.WriteLine("Completed");
   });
  }
 }
}

传送门:

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

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

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

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

以上就是c# 网络编程之tcp的详细内容,更多关于c# 网络编程tcp的资料请关注我们其它相关文章!

(0)

相关推荐

  • 浅谈C#网络编程详解篇

    阅读目录: 基础 Socket编程 多线程并发 阻塞式同步IO 基础 在现今软件开发中,网络编程是非常重要的一部分,本文简要介绍下网络编程的概念和实践. Socket是一种网络编程接口,它是对传输层TCP.UDP通信协议的一层封装,通过友好的API暴露出去,方便在进程或多台机器间进行网络通信. Socket编程 在网络编程中分客户端和服务端两种角色,比如通过打开浏览器访问到挂在Web软件上的网页,从程序角度上来看,即客户端(浏览器)发起了一个Socket请求到服务器端,服务器把网页内容返回到浏览

  • C#网络编程基础之进程和线程详解

    在C#的网络编程中,进程和线程是必备的基础知识,同时也是一个重点,所以我们要好好的掌握一下. 一:概念 首先我们要知道什么是"进程",什么是"线程",好,查一下baike. 进程:是一个具有一定独立功能的程序关于某个数据集合的一次活动.它是操作系统动态执行的基本单元, 在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元. 线程:是"进程"中某个单一顺序的控制流. 关于这两个概念,大家稍微有个印象就行了,防止以后被面试官问到. 二:进程

  • C# Socket网络编程实例

    本文实例讲述了C# Socket网络编程技巧.分享给大家供大家参考.具体分析如下: 客户端要连接服务器:首先要知道服务器的IP地址.而服务器里有很多的应用程序,每一个应用程序对应一个端口号 所以客户端想要与服务器中的某个应用程序进行通信就必须要知道那个应用程序的所在服务器的IP地址,及应用程序所对应的端口号 TCP协议:安全稳定,一般不会发生数据丢失,但是效率低.利用TCP发生数据一般经过3次握手(所有效率低,自己百度三次握手) UDP协议:快速,效率高,但是不稳定,容易发生数据丢失(没有经过三

  • 总结C#网络编程中对于Cookie的设定要点

    花了2天时间,彻底搞清C#中cookie的内容,搞清以下内容将让你对所有网站的cookie都尽在掌握之中. cookieCollection是一个针对一个域所有的cookie的集合 cookeContainer是一个容器,里面可以装多个域的cookie的集合,即一个 cookieContainer可以包含多个cookieCollection,这个容器可以定义大小,决定 最多装多少个cookie,如果装满了还要再装,它会自动剔除原来过期的cookie. 再说到一个cookie的结构: Cookie

  • 详解C# 网络编程系列:实现类似QQ的即时通信程序

    引言: 前面专题中介绍了UDP.TCP和P2P编程,并且通过一些小的示例来让大家更好的理解它们的工作原理以及怎样.Net类库去实现它们的.为了让大家更好的理解我们平常中常见的软件QQ的工作原理,所以在本专题中将利用前面专题介绍的知识来实现一个类似QQ的聊天程序.  一.即时通信系统 在我们的生活中经常使用即时通信的软件,我们经常接触到的有:QQ.阿里旺旺.MSN等等.这些都是属于即时通信(Instant Messenger,IM)软件,IM是指所有能够即时发送和接收互联网消息的软件. 在前面专题

  • c# 网络编程之http

    一.概述 本文目的是通过C#代码提供一个HTTP服务,正常情况下如果我们需要向外界提供HTTP服务,常规做法就是通过ASP.NET来实现,有时我们的应用程序或Windows服务需要向外提供一些简单的HTTP服务就可以自己实现,从而避免部署IIS增加系统复杂性.这里必须强调是一些简单的应用,如果应用比较复杂,涉及到路径解析HTML解析等,还是用WEB方式实现比较靠谱. 将HTTP和UDP.TCP放在同一个系列实际上有一点不合适,因为UDP.TCP属于传输层协议,HTTP属于应用层协议,希望读者首先

  • C#开发之Socket网络编程TCP/IP层次模型、端口及报文等探讨

    1.TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用.http协议在应用层运行. 02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而TCP提供传输保证. 03,网络层(Netwok):网络层协议由一系列协议组成,包括ICMP.IGMP.RIP.OSPF.IP(v4,v6)等. 04,链路层(Link):又称为物

  • 深入学习C#网络编程之HTTP应用编程(下)

    第三篇来的好晚啊,上一篇说了如何向服务器推送信息,这一篇我们看看如何"快好准"的从服务器下拉信息. 网络上有很多大资源文件,比如供人下载的zip包,电影(你懂的),那么我们如何快速的进行下载,大家第一反应肯定就是多线程下载, 那么这些东西是如何做的呢?首先我们可以从"QQ的中转站里面拉一个rar下来". 然后用fiddler监视一下,我们会发现一个有趣的现象: 第一:7.62*1024*1024≈7990914  千真万确是此文件 第二:我明明是一个http链接,t

  • C# 网络编程之UDP

    一.概述 UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.NET框架通过UdpClient.TcpListener .TcpClient这几个类对Socket进行了封装,使其使用更加方便, 本文就通过这几个封装过的类讲解一下相关应用. 二.UDP基本应用 与TCP通信不同,UDP通信是不分服务端和客户端的,通信双方是对等的.为了描述方便,我们把通信双方称为发送方和接收方. 发送方: 首先创建一个UDP对象: string locateIP

  • c# socket网络编程接收发送数据示例代码

    代码分2块,server端: 复制代码 代码如下: class Program    {        static void Main(string[] args)        {            TcpListener lsner = new TcpListener(9000);            lsner.Start();            Console.WriteLine("started in port: 9000");            while

  • 深入学习C#网络编程之HTTP应用编程(上)

    我们学习网络编程最熟悉的莫过于Http,好,我们就从Http入手,首先我们肯定要了解一下http的基本原理和作为,对http的工作原理有 一定程度的掌握,对我们下面的学习都是有很大帮助的. 一: 工作方式 ①:client和server建立可靠的TCP连接. ②:然后client通过Socket向server发送http请求. ③:server端处理请求,返回处理数据. ④:在http1.0中,client与server之间的tcp连接立即断开. 但在http1.1中,因为默认支持"tcp的长连

随机推荐