c# - TCP 客户端指导、重新连接功能等

标签 c# .net-4.0 mono tcpclient

我有一个主要在 mono 上运行的 TCP 客户端,我希望得到一些指导,我认为我做错了一些事情,做了一些不需要的事情,等等。

下面的代码是我用来作为我的疑问的示例的一部分。

  • 正如您所看到的,一旦构造函数被调用,当我实例化 ConcurrentQueues 时,我应该让它自己实例化而不需要从构造函数启动它,还是我当前正在做的方式是正确的,或者这并不重要?

  • 我目前正在运行 3 个线程,我相信我可以将其减少到 2 个甚至 1 个,但我对此感到有点不安全。

    如你所见,我有:

    receiveThread 用于_ReceivePackets 这个控制着所有从 roomserver 接收到的数据

    sendThread 用于_SendPackets 这个控制着必须发送到 roomserver 的所有内容

    responseThread 用于_Response 这将处理从 roomserver 排队的所有响应

    我相信我可以将 _SendPackets_ReceivePackets 合并为一个,并将其添加到我的类 SendPackets 中,无论它是要发送的数据包还是一个数据包已经交付了,我担心的是,如果它有一个巨大的输入/输出,它是否仍能跟上而不把事情搞砸。

    我将 _Response 分开,因为它将处理每种回复类型的更多响应数据,我认为这很好,并且如果我删除它并让_Response 自行处理它,因为某些数据包不会一次性读取。

  • 我应该在 _socket.Connected 中依赖自己到什么程度?

  • 我在部署重新连接时遇到一些问题,大多数时候当我遇到一些连接问题时,它不会触发任何错误,它只是坐在那里,端口打开,就好像它仍然连接一样,如何我应该检测我是否还活着吗?

  • 所有推荐、建议或在线免费阅读 Material ?

旁注:这是一个非常基本的聊天 TCP 客户端实现,用于学习我目前正在研究的内容。

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Collections.Concurrent;
using log4net;

namespace Connection
{
    public class Roomserver
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(Roomserver));

        private ConcurrentQueue<byte[]> RoomserverReceivedPackets = null;
        private ConcurrentQueue<SendPackets> RoomserverSendPackets = null;

        private AutoResetEvent _queueNotifier = new AutoResetEvent(false);
        private AutoResetEvent _sendQueueNotifier = new AutoResetEvent(false);

        public static byte[] myinfo = null;

        private IPAddress _server = null;
        private int _port = 0;
        private int _roomID = 0;

        private Socket _socket;
        private Status _status = Status.Disconnected;

        private Thread responseThread = null;
        private Thread receiveThread = null;
        private Thread sendThread = null;
        private EndPoint _roomServer = null;

        public bool Connected
        {
            get { return _socket.Connected; }
        }

        public Status GetStatus
        {
            get { return _status; }
        }

        public Roomserver(IPAddress server, int port)
        {
            this._server = server;
            this._port = port;

            RoomserverReceivedPackets = new ConcurrentQueue<byte[]>();
            RoomserverSendPackets = new ConcurrentQueue<SendPackets>();
        }

        public Roomserver(IPAddress server, int port, int roomID)
        {
            this._server = server;
            this._port = port;
            this._roomID = roomID;

            RoomserverReceivedPackets = new ConcurrentQueue<byte[]>();
            RoomserverSendPackets = new ConcurrentQueue<SendPackets>();
        }

        public bool Connect()
        {
            try
            {
                if (_status != Status.Disconnected)
                    this.Disconnect();

                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint remoteEndPoint = new IPEndPoint(_server, _port);

                _socket.Connect(remoteEndPoint);
                _status = Status.Connect;
                _roomServer = (EndPoint)remoteEndPoint;

                receiveThread = new Thread(_ReceivePackets);
                receiveThread.Start();

                sendThread = new Thread(_SendPackets);
                sendThread.Start();

                responseThread = new Thread(_Response);
                responseThread.Start();

                return _socket.Connected;
            }
            catch (SocketException se)
            {
                logger.Error("Connect: " + se.ToString());
                _status = Status.Disconnected;

                return false;
            }
            catch (Exception ex)
            {
                logger.Error("Connect: " + ex.ToString());
                _status = Status.Disconnected;

                return false;
            }
        }

        public bool Disconnect()
        {
            if (_socket.Connected)
            {
                _status = Status.Disconnected;

                if (receiveThread != null && receiveThread.IsAlive)
                {
                    receiveThread.Abort();
                }

                if (responseThread != null && responseThread.IsAlive)
                {
                    responseThread.Abort();
                }

                if (sendThread != null && sendThread.IsAlive)
                {
                    sendThread.Abort();
                }

                try
                {
                    _socket.Close();
                    return true;
                }
                catch (Exception ex)
                {
                    logger.Info("Disconnect " + ex.ToString());
                    _status = Status.Disconnected;
                    return true;
                }
            }
            else
            {
                logger.Info("Not connected ...");
                _status = Status.Disconnected;
                return true;
            }
        }

        public bool SendData(byte[] bytes, bool delay)
        {
            try
            {
                SendPackets data = new SendPackets()
                {
                    Data = bytes,
                    Delay = delay
                };
                RoomserverSendPackets.Enqueue(data);
                _sendQueueNotifier.Set();
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("SendData " + ex.ToString());
                return false;
            }
        }

        private void _SendPackets()
        {
            while (_socket.Connected)
            {
                _sendQueueNotifier.WaitOne();
                while (!RoomserverSendPackets.IsEmpty)
                {
                    SendPackets packet = null;
                    if (RoomserverSendPackets.TryDequeue(out packet))
                    {
                        try
                        {
                            if (packet.Delay)
                            {
                                Thread.Sleep(1000);
                                _socket.Send(packet.Data);
                            }
                            else
                                _socket.Send(packet.Data);
                        }
                        catch (SocketException soe)
                        {
                            logger.Error(soe.ToString());
                        }
                    }
                }
            }
        }

        private void _ReceivePackets()
        {
            bool extraData = false;
            MemoryStream fullPacket = null;
            int fullPacketSize = 0;

            while (_socket.Connected)
            {
                try
                {
                    byte[] bytes = new byte[65536];
                    int bytesRead = _socket.ReceiveFrom(bytes, ref _roomServer);
                    int packetSize = 0;
                    int reply = 0;

                    byte[] data = new byte[bytesRead];
                    Array.Copy(bytes, data, bytesRead);

                    MemoryStream bufferReceived = new MemoryStream(data, 0, data.Length);
                    using (var reader = new BinaryReader(bufferReceived))
                    {
                        packetSize = (int)reader.ReadInt32() + 4;
                        reply = (int)reader.ReadByte();
                    }

                    if (!extraData && packetSize <= bytesRead)
                    {
                        if (data.Length > 0)
                        {
                            RoomserverReceivedPackets.Enqueue(data);
                            _queueNotifier.Set();
                        }
                    }
                    else
                    {
                        if (!extraData)
                        {
                            fullPacket = new MemoryStream(new byte[packetSize], 0, packetSize);
                            fullPacket.Write(data, 0, data.Length);
                            fullPacketSize = data.Length;
                            extraData = true;
                        }
                        else
                        {
                            if (fullPacketSize < fullPacket.Length)
                            {
                                int left = (int)fullPacket.Length - fullPacketSize;
                                fullPacket.Write(data, 0, (left < data.Length) ? left : data.Length);
                                fullPacketSize += (left < data.Length) ? left : data.Length;

                                if (fullPacketSize >= fullPacket.Length)
                                {
                                    extraData = false;
                                    RoomserverReceivedPackets.Enqueue(fullPacket.ToArray());
                                    _queueNotifier.Set();
                                    fullPacket.Close();
                                }
                            }
                        }
                    }
                }
                catch (SocketException soe)
                {
                    logger.Error("_ReceivePackets " + soe.ToString());
                }
                catch (Exception ex)
                {
                    logger.Error("_ReceivePackets " + ex.ToString());
                }
            }
        }

        private void _Response()
        {
            while (_socket.Connected)
            {
                _queueNotifier.WaitOne();

                while (!RoomserverReceivedPackets.IsEmpty)
                {
                    byte[] data = null;
                    if (RoomserverReceivedPackets.TryDequeue(out data))
                    {
                        MemoryStream bufferReceived = new MemoryStream(data, 0, data.Length);
                        using (var reader = new BinaryReader(bufferReceived))
                        {
                            int packetSize = (int)reader.ReadInt32();
                            byte reply = reader.ReadByte();

                            switch (reply)
                            {
                                case 0x01: // Login request
                                    break;
                                case 0x02: // Login accepted
                                    break;
                                case 0x03: // Enter room
                                    break;
                                case 0x04: // Members list
                                    break;
                                case 0x05: // Send Chat
                                    break;
                                case 0x06: // Receive Chat
                                    break;
                                case 0x07: // Receive Announcement
                                    break;
                                case 0x08: // Send Announcement
                                    break;
                                case 0x09: // Wrong password errors
                                    _status = Status.RoomError;
                                    break;
                                case 0x10: // Send Whisper
                                    break;
                                case 0x11: // Receive Whisper
                                    break;
                                case 0x12: // Leave Room
                                    break;
                                case 0x13: // Disconnect
                                    break;
                            }
                        }
                    }
                }
            }
        }
    }
}

在我的另一个类上:

public class SendPackets
{
    public byte[] Data { get; set; }
    public bool Delay { get; set; }
}

public enum Status
{
    Disconnected = 0,
    Connect,
    EnterRequest,
    RoomError,
    Connected
}

最佳答案

  • 使用 Dictionary<int, ICommandHandler>而不是你的 switch 语句
  • 切换到异步套接字
  • 阅读单一职责原则。
  • 使用.NET naming convention

如果您想要更具体的答案,请回来询问更具体的问题。

更新以回答评论

而不是:

switch (reply)
{
    case 0x01: // Login request
        break;
    case 0x02: // Login accepted

做:

public interface ICommandHandler
{
    void Handle(byte[] packet);
}

public class LoginHandler : ICommandHandler
{
    public void Handle(byte[] packet) 
    {
        // handle packet data here
    }
}

var myHandler = new LoginHandler();
myServer.Register(1, myHandler);

然后在你的套接字类中:

public class MyServer
{
     Dictionary<int, ICommandHandler> _handlers;

     public void Register(int functionId, ICommandHandler handler)
     {
          _handlers[functionId] = handler;
     }

     private void _Response()
     {

           // .. alot of stuff ..
           _handlers[reply].Handle(memoryStream);

     }

请注意,该示例还远未完成,您可能希望发送上下文类而不仅仅是内存流。

关于c# - TCP 客户端指导、重新连接功能等,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7148058/

相关文章:

c# - 如何使用 Linq 从 List<Object> 中获取第一个对象

c# - 如何合并 ThreadLocal<T> 中的所有值?

c# - 什么时候使用 Partitioner 类?

c# - UDP通信快速填满内存

c# - 单声道移植问题

c# - 如果我有 _full_ 绝对 uri,如何获取对 Azure blob/文件的 GetFileReference?

c# - 枚举多次的 .Net 方法应该接受哪种类型的集合作为参数?

c# - 添加到组合框的文本突出显示

c# - .net framework 4.0 c# 中的文件压缩

linux - stunnel https 被重定向到 http