应用TcpListener实现的socket服务器端

前言

项目中要实现一个简单的socket服务器端,采用了TcpListener这个类。除了基本的功能之外,有几处需要注意的点。

  1. 要能同时接收多个客户端的连接,当然,不需要几千个那么多。
  2. 要能探测到客户端的断开。
  3. 要能关闭服务器端的监听。

这几个点之间,2和3我没有找到很好的方法,是通过捕获异常的方法解决的。

重点功能

要能同时接收多个客户端的连接

MSDN上面的代码例子是连接一个客户端的情况,我需要可以连接多个客户端,采用了多线程的方式,即连接一个客户端之后,把处理客户端消息的部分用一个线程处理,这样可以继续新的监听,核心代码如下:

 private void ThreadListen()
        {
            while (this.StatusOn)
            {
                try
                {
                    TcpClient client = this.serverListener.AcceptTcpClient();
                    this.clientList.Add(client);
                    this.ShowMsg(string.Format("客户端连接成功! ip = {0} port = {1}", ((IPEndPoint)client.Client.RemoteEndPoint).Address, ((IPEndPoint)client.Client.RemoteEndPoint).Port));
                    Thread t1 = new Thread(() => ThreadHandleMsg(client));
                    t1.Start();
                }
                catch (Exception ex)
                {
                    Log.Error("", ex);
                }
            }
        }

要能探测到客户端的断开

在如下代码之处,while循环的条件会阻塞掉,等待客户端的输入(i = stream.Read(bytes, 0, bytes.Length)) != 0,这个不知道如何判断客户端断开,所以用了try catch比较low的办法。

Byte[] bytes = new Byte[1024];
String data = null;
NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
    if (client == null || !client.Connected)
    {
        break;
    }
    data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
    this.ShowMsg(string.Format("收到消息: {0}", data));
    data = data.ToUpper();
    byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
    stream.Write(msg, 0, msg.Length);
    this.ShowMsg(string.Format("返回消息: {0}", data));
}                    

要能关闭服务器端的监听

另外也没有发现如何关闭服务器端的监听,我看了一下stackoverflow,也没发现特别好的办法。代码如下:

public void Close()
{
    this.StatusOn = false;
    foreach (var client in this.clientList)
    {
        if (client != null && client.Connected)
        {
            client.Close();
        }
    }
    this.clientList.Clear();
    this.serverListener.Server.Close();
    this.serverListener.Stop();
    this.ShowMsg("停止监视,关闭连接");
}

全部代码

using log4net;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
namespace srtc_attools.Bll
{
    public class TcpServer
    {
        private static TcpServer inst;
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private List<TcpClient> clientList;
        private TcpListener serverListener;
        public Action<string> ShowMsg { get; set; }
        public bool StatusOn { get; set; }
   
        private TcpServer() { }
        public static TcpServer GetInst()
        {
            if (inst == null)
            {
                inst = new TcpServer();
            }
            return inst;
        }
        private Thread threadListen;
        public void Open(string ip, int port)
        {
            this.clientList = new List<TcpClient>();
            this.StatusOn = true;
            this.ShowMsg("开始监视,等待连接");
            this.serverListener = new TcpListener(IPAddress.Parse(ip), port);
            this.serverListener.Start();
            threadListen = new Thread(() => ThreadListen());
            threadListen.Start();
        }

        private void ThreadListen()
        {
            while (this.StatusOn)
            {
                try
                {
                    TcpClient client = this.serverListener.AcceptTcpClient();
                    this.clientList.Add(client);
                    this.ShowMsg(string.Format("客户端连接成功! ip = {0} port = {1}", ((IPEndPoint)client.Client.RemoteEndPoint).Address, ((IPEndPoint)client.Client.RemoteEndPoint).Port));
                    Thread t1 = new Thread(() => ThreadHandleMsg(client));
                    t1.Start();
                }
                catch (Exception ex)
                {
                    Log.Error("", ex);
                }
            }
        }
        private void ThreadHandleMsg(TcpClient client)
        {
            if (client.Connected)
            {
                try
                {
                    Byte[] bytes = new Byte[1024];
                    String data = null;
                    NetworkStream stream = client.GetStream();
                    int i;
                    while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        if (client == null || !client.Connected)
                        {
                            break;
                        }
                        data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                        this.ShowMsg(string.Format("收到消息: {0}", data));
                        data = data.ToUpper();
                        byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
                        stream.Write(msg, 0, msg.Length);
                        this.ShowMsg(string.Format("返回消息: {0}", data));
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("客户端发生错误
", ex);
                    if(this.clientList.Contains(client))
                    {
                        this.clientList.Remove(client);
                        this.ShowMsg(string.Format("客户端断开连接! ip = {0} port = {1}", ((IPEndPoint)client.Client.RemoteEndPoint).Address, ((IPEndPoint)client.Client.RemoteEndPoint).Port));
                    }
                }
            }
        }
        public void Close()
        {
            this.StatusOn = false;
            foreach (var client in this.clientList)
            {
                if (client != null && client.Connected)
                {
                    client.Close();
                }
            }
            this.clientList.Clear();
            this.serverListener.Server.Close();
            this.serverListener.Stop();
            this.ShowMsg("停止监视,关闭连接");
        }
    }
}
原文地址:https://www.cnblogs.com/wardensky/p/5734106.html