• C#之Socket通信


    0.虽然之前在项目中也有用过Socket,但始终不是自己搭建的,所以对Server,Clinet端以及心跳,断线重连总没有很深入的理解,现在自己搭建了一遍加深一下理解。

    服务端使用WPF界面,客户端使用控制台。实现了心跳,断线重连,一个服务端对应多个客户端的功能。

    一.服务端

    1.1 先创建一个Socket实例,并绑定到20000端口号;通过Listen方法开始监听并设置最大监听数量。

    //新建一个Socket服务端实例,并绑定到20000端口
    socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socketServer.Bind(new IPEndPoint(IPAddress.Any, 20000));
    
    //设置最大监听数量
    socketServer.Listen(10);

    1.2 当有客户端成功连接,则会通过Accept方法生产一个新的Socket实例;此时可开启心跳定时器,并开启一个线程接收消息。

    //开启一个线程监听客户端
    Thread thd = new Thread(new ThreadStart(ListenSocket));
    thd.Start();
    
    /// <summary>
    /// 开始监听
    /// </summary>
    private void ListenSocket()
    {
        try
        {
            while (true)
            {
                Socket _socket = socketServer.Accept();
    
                //开始心跳
                System.Timers.Timer heartbeatTimer = new System.Timers.Timer();
                heartbeatTimer.Interval = 60000;
                heartbeatTimer.Elapsed += new System.Timers.ElapsedEventHandler((s, e) => heartbeatTimerIsUp(s, e, _socket));
                heartbeatTimer.Start();
    
                Thread thdReceive = new Thread(new ParameterizedThreadStart(thdRevMethod));
                thdReceive.Start(_socket);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("程序出现异常:" + ex);
        }
    }

    1.3 接收消息与发送消息的代码如下

    /// <summary>
    /// 接收消息
    /// </summary>
    /// <param name="obj"></param>
    private void thdRevMethod(object obj)
    {
        Socket _socket = obj as Socket;
        try
        {
            while (true)
            {
                byte[] resByte = new byte[1024];
                int resInt = _socket.Receive(resByte);
                if (resInt > 0)
                {
                    string res = Encoding.Default.GetString(resByte, 0, resInt);///接收消息后操作
                }
            }
        }
        catch (SocketException sex)
        {
            if (sex.SocketErrorCode == SocketError.ConnectionReset)
            {
                //当客户端断开连接,从客户端列表中移除该客户端
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("程序出现异常:" + ex);
        }
    }
    
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="msg"></param>
    /// <param name="ipAndPord"></param>
    public bool sendMsg(string msg,string ipAndPord)
    {
        try
        {
            Socket _socket = socketTimerDic.SingleOrDefault(r => string.Equals(r.Key.RemoteEndPoint.ToString(), ipAndPord)).Key;
            if (_socket != null)
            {
                byte[] byteStr = Encoding.Default.GetBytes(msg);
                _socket.Send(byteStr);
                return true;
            }
            else
            {
                return false;
            }
        }
        catch (Exception)
        {
            return false;
        }
    }

    二.客户端

    2.1 先创建一个Socket实例,并连接到服务端所在的ip端口;连接成功后开启心跳定会器并开启发送和接收消息的两个线程。

    // 新建客户端实例,并连接到服务端所在的端口号
    socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
    socketClient.Connect("127.0.0.1", 20000);
    Console.WriteLine("成功连接到服务端");
    heartbeatTimer.Start();
    
    //开启一个线程发送消息
    Thread thdSend = new Thread(new ThreadStart(thdSendMethod));
    thdSend.Start();
    
    //开启一个线程接收信息
    Thread thdRev = new Thread(new ThreadStart(thdRevMethod));
    thdRev.Start();

    2.2 发送消息和接收消息的代码如下,当服务端断开会在接收消息的线程中触发sex.SocketErrorCode == SocketError.ConnectionReset的异常,此时捕获到后开启重连定时器即可

    /// <summary>
    /// 接收消息
    /// </summary>
    private static void thdRevMethod()
    {
        try
        {
            while (true)
            {
                byte[] resByte = new byte[1024];
                int resInt = socketClient.Receive(resByte);
                if (resInt > 0)
                {
                     Console.WriteLine(Encoding.Default.GetString(resByte,0, resInt));
                }
            }
    
        }
        catch (SocketException sex)
        {
            if (sex.SocketErrorCode == SocketError.ConnectionReset)
            {
                Console.WriteLine("服务端断开!5s后重连!");
                reconnectTimer.Start();
                heartbeatTimer.Stop();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("程序出现异常:" + ex);
            heartbeatTimer.Stop();
        }
    }
    
    //发送信息
    private static void thdSendMethod()
    {
        try
        {
            while (true)
            {
                string res = Console.ReadLine();
                byte[] resByte = Encoding.Default.GetBytes(res);
                socketClient.Send(resByte);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("程序出现异常:" + ex);
            heartbeatTimer.Stop();
        }
    }    

    三.运行示例

    四..总结

    假如代码中有错误的地方,希望可以帮忙指出来改之。

    其中要注意的地方如下:
    1.WPF关闭窗口后,需要通过 Environment.Exit(0);来结束掉当前整个服务端进程,否则Socket开启的接收和发送进程将还在,客户端也不会检测到服务端断开。

    2.在SocketException中捕获异常sex.SocketErrorCode == SocketError.ConnectionReset时说明客户端/服务端连接断开了,需要进行重连等操作。

    3.在接收到消息时候需要用GetString(byte[] bytes, int index, int count)指定长度,而不该使用GetString(byte[] bytes),否则可能出现很多空格。

    4.定时器假如需要传递参数,可以通过设置全局变量,或者(s, e) => heartbeatTimerIsUp(s, e, _socket)单独传递一个变量到方法中。

    源码下载地址如下:SocketDemo.rar

  • 相关阅读:
    【笔记】rocketMQ了解
    【笔记】正则表达式·记②贪婪匹配、非贪婪匹配、独占模式和量词
    source 1.5 中不支持 diamond 运算符(中文版idea)
    【笔记】Linux基础指令
    maven BUILD FAILURE
    SELECT...FROM 表 a,( SELECT...FROM...WHERE...) tc...的一些注意
    dbeaver导出结果集中乱码
    【笔记】正则表达式·记①元字符
    maven报错:501 HTTPS Required
    【笔记】正则表达式·记③分组与引用
  • 原文地址:https://www.cnblogs.com/liyijin/p/8733453.html
Copyright © 2020-2023  润新知