• 应用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("停止监视,关闭连接");
            }
        }
    }
    
  • 相关阅读:
    ArcGIS.Server.9.3.DotNet的ADF与ASP.NET AJAX
    ArcGIS.Server.9.3和ArcGIS API for JavaScript保存自定义图形(十)
    android开发我的新浪微博客户端载入页面UI篇(1.1)
    ArcGIS.Server.9.3和ArcGIS API for JavaScript实现距离量算和面积量算(九)
    ArcGIS.Server.9.3和ArcGIS API for JavaScript实现点、线、面的buffer分析(十一)
    android开发我的新浪微博客户端载入页面sqlite篇(1.2)
    市净率PB
    转 未来的GDI:WPF技术纵览
    转 LUA语言学习教程
    sl下的两种跨线程访问方式
  • 原文地址:https://www.cnblogs.com/wardensky/p/5734106.html
Copyright © 2020-2023  润新知