Socket 简易搭建

虽然之前在项目中也有用过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

posted @ 2020-03-31 12:31  惜时、笃学  阅读(832)  评论(0编辑  收藏  举报