C#之Socket通讯

0.虽然以前在项目中也有用过Socket,但始终不是本身搭建的,因此对Server,Clinet端以及心跳,断线重连总没有很深刻的理解,如今本身搭建了一遍加深一下理解。socket

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

一.服务端线程

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

//新建一个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实例;此时可开启心跳定时器,并开启一个线程接收消息。blog

//开启一个线程监听客户端
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;
    }
}

二.客户端ip

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

// 新建客户端实例,并链接到服务端所在的端口号
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();
    }
}    

三.运行示例string

四..总结

假如代码中有错误的地方,但愿能够帮忙指出来改之。

其中要注意的地方以下:
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

相关文章
相关标签/搜索