C#中的Socket需要注意的细节

在c#中Task是什么

在C#中,Task 是一个表示异步操作的类,它是.NET Framework中的一个核心组件,用于实现异步编程模型。Task 允许开发者编写非阻塞的代码,这样可以提高应用程序的响应性和性能。

以下是一些关于 Task 的关键点:

  1. 表示异步操作Task 对象代表了一个可能已经完成或尚未完成的异步操作。

  2. 返回值Task 可以返回一个值,通过 Task<TResult> 实现,其中 TResult 是结果的类型。

  3. 状态Task 对象有几种状态,如 RunningWaitingToRunRanToCompletionFaulted(出现错误)、Canceled(被取消)等。

  4. 异常处理如果异步操作中发生异常,Task 对象会捕获这些异常,并且可以通过 Task.Exception 属性访问它们。

  5. 等待完成:可以使用 Task.Wait() 或者 await 关键字来等待 Task 完成。

  6. 取消:可以通过调用 Task 对象的 Cancel() 方法来尝试取消一个正在运行的异步操作。

  7. 继续操作:可以使用 ContinueWith 方法来链式调用,当一个 Task 完成时,自动启动另一个 Task

  8. 并行执行:可以使用 Task.WhenAllTask.WhenAny 来处理多个 Task 的并行执行。

  9. 配置Task 可以配置为同步或异步执行,以及配置其调度器等。

  10. 使用场景Task 广泛用于需要异步执行的场合,如I/O操作、网络请求、长时间运行的计算任务等

using System;
using System.Threading.Tasks;
​
class Program
{
    static async Task Main(string[] args)
    {
        Task task = Task.Run(() => DoSomeWork());
        await task; // 等待任务完成
​
        Console.WriteLine("Task completed.");
    }
​
    static void DoSomeWork()
    {
        // 模拟一些工作
        Task.Delay(1000).Wait();
        Console.WriteLine("Work done.");
    }
}

DoSomeWork 方法在一个新的 Task 中异步执行,Main 方法使用 await 关键字等待这个 Task 完成。这种方式允许 Main 方法在等待时释放控制权,使得应用程序可以继续响应其他事件或操作。

Connect和BeginConnect的区别

在C#中,ConnectBeginConnect 是用于建立网络连接的方法,通常在 Socket 类中使用。它们的主要区别在于同步和异步操作:

  1. Connect

    • Connect 是一个同步方法。

    • 当调用 Connect 方法时,当前线程会被阻塞,直到连接建立成功或失败。

    • 它返回 void,并且可能引发异常,例如 SocketException,如果连接失败。

  2. BeginConnect

    • BeginConnect 是一个异步方法。

    • 当调用 BeginConnect 方法时,它立即返回,不阻塞当前线程,允许应用程序继续执行其他任务

    • 它返回一个 IAsyncResult 对象,这个对象可以用于跟踪异步操作的进度。

    • 为了完成连接,你需要使用 EndConnect 方法,它将等待异步操作完成,并返回一个 SocketError 枚举值来指示操作的结果。

使用 Connect

using System;
using System.Net.Sockets;
​
class Program
{
    static void Main(string[] args)
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            socket.Connect("www.example.com", 80); // 同步连接
            Console.WriteLine("Connected to the server.");
            // 进行数据传输...
        }
        catch (SocketException e)
        {
            Console.WriteLine("Connection failed: " + e.Message);
        }
    }
}

使用 BeginConnect

using System;
using System.Net.Sockets;
​
class Program
{
    static void Main(string[] args)
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.BeginConnect("www.example.com", 80, new AsyncCallback(ConnectCallback), socket);
​
        // 应用程序可以继续执行其他任务...
​
        Console.ReadLine(); // 等待用户输入,防止程序退出
    }
​
    private static void ConnectCallback(IAsyncResult ar)
    {
        try
        {
            Socket socket = (Socket)ar.AsyncState;
            socket.EndConnect(ar); // 完成连接
            Console.WriteLine("Connected to the server.");
            // 进行数据传输...
        }
        catch (Exception e)
        {
            Console.WriteLine("Connection failed: " + e.Message);
        }
    }
}

在异步示例中,BeginConnect 允许应用程序在等待连接建立的同时执行其他任务。当连接建立后,ConnectCallback 回调方法会被调用,以完成连接并进行后续操作。这种方式特别适用于需要高响应性的应用程序,例如用户界面应用程序。

EndConnect 方法

在C#中,EndConnect 方法是与 BeginConnect 方法配合使用的,用于完成一个异步的网络连接操作。当你使用 BeginConnect 方法开始一个异步连接时,EndConnect 将被用来确定连接是否成功完成,或者在连接过程中是否发生了错误。

以下是 EndConnect 方法的一些关键点:

  1. 用途EndConnect 用于结束由 BeginConnect 方法启动的异步连接尝试。

  2. 参数EndConnect 方法接受一个 IAsyncResult 对象作为参数,这个对象是由 BeginConnect 方法返回的,用于跟踪异步操作的状态。

  3. 返回值EndConnect 不返回任何值,但它会设置 Socket 对象的状态,以便你可以检查连接是否成功。

  4. 错误处理:如果连接失败,EndConnect 会抛出 SocketException。然而,Socket 类提供了 SocketError 枚举,你可以在调用 EndConnect 之前检查 IAsyncResult 对象的 SocketError 属性来提前发现错误。

  5. 同步行为:尽管 EndConnect 是用来结束异步操作的,但它本身是同步的。调用 EndConnect 的线程将会被阻塞,直到异步操作完成。

  6. 使用场景EndConnect 通常在异步回调方法中被调用,例如在 BeginConnect 方法的 AsyncCallback 委托中。

using System;
using System.Net.Sockets;
​
class Program
{
    static void Main(string[] args)
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        
        // 开始异步连接
        socket.BeginConnect("www.example.com", 80, new AsyncCallback(ConnectCallback), socket);
        
        // 应用程序可以继续执行其他任务...
        Console.ReadLine(); // 等待用户输入,防止程序退出
    }
​
    private static void ConnectCallback(IAsyncResult ar)
    {
        Socket socket = (Socket)ar.AsyncState;
        
        try
        {
            // 完成异步连接
            socket.EndConnect(ar);
            Console.WriteLine("Connected to the server.");
​
            // 检查是否有错误发生
            if (ar.IsCompleted && ar.AsyncWaitHandle.WaitOne(0))
            {
                SocketError error = (SocketError)ar.AsyncState;
                if (error != SocketError.Success)
                {
                    throw new SocketException((int)error);
                }
            }
​
            // 进行数据传输...
        }
        catch (Exception e)
        {
            Console.WriteLine("Connection failed: " + e.Message);
        }
    }
}

BeginConnect 启动了异步连接尝试,并且指定了一个回调方法 ConnectCallback。当连接尝试完成时,ConnectCallback 会被调用,然后 EndConnect 被用来结束连接尝试,并检查是否成功。如果连接成功,应用程序可以继续进行数据传输或其他操作。如果连接失败,异常将被捕获并处理。

长连接 短连接区别

长连接(Long Connection)和短连接(Short Connection)是网络通信中两种不同的连接模式,它们在建立连接、使用资源、适用场景等方面有所区别:

  1. 连接建立

    • 长连接:一旦建立,连接会保持打开状态,直到客户端或服务器端明确关闭连接。适用于需要持续通信的场景。
    • 短连接:每次通信都需要建立一个新的连接,通信完成后立即关闭连接。适用于偶尔的、短暂的通信。
  2. 资源消耗

    • 长连接:由于连接保持打开状态,可能会占用更多的服务器资源,尤其是在大量客户端同时连接时。
    • 短连接:每次通信结束后释放连接资源,对服务器资源的占用相对较少,但频繁的连接和断开可能会增加网络延迟。
  3. 适用场景

    • 长连接:适用于需要频繁通信、实时性要求高的应用,如即时通讯、在线游戏、实时数据传输等。
    • 短连接:适用于通信频率较低、数据量小的场景,如HTTP请求、FTP传输等。
  4. 连接稳定性

    • 长连接:由于连接持续存在,通常可以提供更稳定的通信环境。
    • 短连接:每次通信都需要重新建立连接,可能会受到网络波动的影响,稳定性相对较低。
  5. 维护成本

    • 长连接:需要更多的维护工作,如心跳检测、连接超时处理等,以确保连接的持续有效性。
    • 短连接:由于连接生命周期短,维护成本相对较低。
  6. 性能影响

    • 长连接:可以减少连接建立和断开的开销,提高通信效率,但可能会因为长时间占用资源而影响性能。
    • 短连接:每次通信都需要建立和断开连接,可能会增加通信的延迟,但可以更灵活地分配资源。
  7. 安全性

    • 长连接:由于连接保持时间较长,可能需要更多的安全措施来防止潜在的安全风险。
    • 短连接:每次通信结束后连接即关闭,可以降低一些安全风险,但也需要确保每次通信的安全。

Socket 断线重连


    // 拿到最新的状态 
    Socket socket = new Socket(
        AddressFamily.InterNetwork,
        SocketType.Stream,
        ProtocolType.Tcp);

    // 三次握手
    socket.Connect(new IPEndPoint(IPAddress.Parse("192.168.2.1"), 102));
    Console.WriteLine(socket.Connected);// 保留最后一次操作时的状态,不是实时的状态 

    while (true)
    {
        // 心跳请求
        Thread.Sleep(1000);
        bool state = !(!socket.Connected || (socket.Poll(1, SelectMode.SelectRead) && socket.Poll(1, SelectMode.SelectWrite) && (socket.Available == 0)));// 返回True   :连接正常
        if (state)
        {
            Console.WriteLine("连接正常");
            socket.Shutdown(SocketShutdown.Both);
            socket.Dispose();
        }
        else
        {
            Console.WriteLine("连接断开");
            socket = new Socket(
                   AddressFamily.InterNetwork,
                   SocketType.Stream,
                   ProtocolType.Tcp);
            try
            {
                // 大概20秒时间
                //socket.Connect(new IPEndPoint(IPAddress.Parse("192.168.2.1"), 102));
            }
            catch (Exception ex)
            {
                Console.WriteLine("重新连接失败");
                Console.WriteLine(ex.Message);
            }
            var result = socket.BeginConnect(new IPEndPoint(IPAddress.Parse("192.168.2.1"), 102), new AsyncCallback(r =>
            {
                // 
                Console.WriteLine(r.AsyncState);
                if (!r.IsCompleted)   // 连接请求的动作 执行完成    不包括连接状态
                    socket.EndConnect(r);
                Console.WriteLine("回调执行");
            }), null);
            bool s = result.AsyncWaitHandle.WaitOne(1000);
            if (s)// 连接成功后
            {
                Console.WriteLine("重新连接");
            }
            else
            {
                // 连接的时候  异步,在1秒钟时间内   没有连接成功   跳出
                try
                {
                    //socket.EndConnect(result); // 销毁之前的连接请求    卡线程
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                Console.WriteLine("重连失败");
            }
        }
        //重连  使用    发送业务请求  执行连接判断    
    }

Socket TCP 服务端

 SocketType.Stream,
  ProtocolType.Tcp
 Socket socket = new Socket(
       AddressFamily.InterNetwork,
        SocketType.Stream,
        ProtocolType.Tcp);
 socket.Bind(new IPEndPoint(IPAddress.Any, 9090));// IP  端口   指定
  127.0.0.1    localhost  192.168...
socket.Listen(1);// 启动服务监听    参数的意思:等待连接的数量

 Task.Run(() =>
 {
     while (true)
    {
        Socket client = socket.Accept();// 同步    卡住线程
        clientList.Add(client);

        Task.Run(() =>
         {
            while (true)
            {
                 // 接收到一个消息后  转发出去   应用层协议
                byte[] buffer = new byte[1024];
                client.Receive(buffer);// 卡线程
                string msg = Encoding.UTF8.GetString(buffer);
                msg = msg.Replace("\0", "");
                 Console.WriteLine("从客户端接收到:" + msg);

                // PLC  接收到特定的报文    byte[]
                // 执行处理逻辑  I0.0   
               // 将值马上返回给客户端 

                msg += "  - Server";
                client.Send(Encoding.UTF8.GetBytes(msg));// 

                //using (Socket client = socket.Accept())
                 //{
                //    client.Shutdown();
                 //}
                 //client.Dispose();
             }//
        });
    }
 });
 var state = socket.BeginAccept(new AsyncCallback(result =>
   {
       Console.WriteLine(result.IsCompleted);
   }), null);
 state.AsyncWaitHandle.WaitOne();// 等待执行
 Console.WriteLine("服务监听已启动...");

Socket TCP 客户端

               // 创建一个TCP客户端 PLC  服务端 客户
                using (Socket socket = new Socket(
                    AddressFamily.InterNetwork,
                     SocketType.Stream,
                     ProtocolType.Tcp))
                {
                    socket.ReceiveTimeout = 5000;
                    socket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9080));// 成功
                    // 为什么马上就下线了   using   Dispose

                    byte[] bytes = Encoding.UTF8.GetBytes("Request");
                    // 成功
                    socket.Send(bytes);

                    // 响应
                    byte[] buffer = new byte[1024];
                    try
                    {
                        socket.Receive(buffer);// 服务端未响应;链路异常    
                                               // 要求:在一定时间内  如果未接收到响应   超时处理
                        string msg = Encoding.UTF8.GetString(buffer).Replace("\0", "");
                        Console.WriteLine(Encoding.UTF8.GetString(buffer));
                        //socket.Shutdown(SocketShutdown.Both);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("接收超时");
                        Console.WriteLine(ex.Message);
                    }
                }

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值