我有一个主要在单声道上运行的TCP客户端,我希望得到一些指导,我认为我做错了什么,不需要的东西等等。
下面的代码是我用来作为我怀疑的样本的一部分。
正如您所看到的那样,一旦构造函数被调用,当我实例化ConcurrentQueues时,我是否应该自己实例化它不需要从构造函数启动它或者我当前正在做的方式是正确的或者这不重要吗?
我目前有三条赛跑,我相信我可以减少到2分甚至一分,但我这样做有点不安全。
如你所见,我有:
receiveThread
的{p> _ReceivePackets
这个控制来自roomserver的所有收到的数据
sendThread
的{p> _SendPackets
这个控制必须发送到roomserver的所有内容
responseThread
的{p> _Response
这将处理从roomserver排队的所有响应
我相信我可以将_SendPackets
与_ReceivePackets
合并为一个并增加到我的班级SendPackets
,这是一个要发送的数据包或一个已发送的数据包,我害怕的是什么如果它有一个巨大的输入/输出,如果它仍然可以跟上你搞乱的事情。
我将_Response
分开,因为它会处理每种类型的回复更多的响应数据,我认为这很好,如果我删除它并让{{1由于一些数据包不会被一次性重写,所以自己处理它。
我应该把自己放在_Response
?
我在部署重新连接时遇到了一些问题,大部分时间我遇到连接问题时,它不会触发任何错误,它只是在端口打开时就好像它仍然连接一样,如何我应该检测一下我是否还活着吗?
所有建议,建议或在线免费阅读材料?
附注:这是我正在研究的聊天tcp客户端的一个非常基本的实现。
_socket.Connected
在我的其他课程上:
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;
}
}
}
}
}
}
}
}
答案 0 :(得分:3)
Dictionary<int, ICommandHandler>
代替您的switch语句如果您想要更具体的答案,请回来询问更具体的问题。
更新以回复评论
而不是:
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);
然后在你的socket类中:
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);
}
请注意,该示例远未完成,您可能希望发送上下文类而不仅仅是内存流。
答案 1 :(得分:1)
我应该把自己放在_socket.Connected?
中
Connected
属性为您提供有关上一次操作的套接字状态的信息,因此如果套接字在您上次尝试读取或写入后更改状态,则{{1会给你错误的(旧)状态。
根据documentation,您应该进行零长度发送以使.NET更新套接字状态。此Connected
操作的成功将告诉您套接字是否仍然连接。
答案 2 :(得分:1)
通常,在此类应用程序中只需要一个线程进行通信。如果您的所有应用程序都是聊天,那么整个事情可能是单线程的。如果有读/写操作,它会阻塞你的控制台,但你可以通过执行异步读/写调用或将超时置于阻塞操作来解决这个问题。在我看来,你对线程有点过分热心。我给新程序员的建议是如果你不确定是否需要多个线程,先从单线程方法开始,当你发现存在阻塞的区域或者可以通过多线程改进性能的区域时,请切换。不要事先做好。
我看到你使用ReceiveFrom,它适用于无连接协议。请尝试使用基本接收。您应该指定要接收的字节数,否则可能会溢出接收缓冲区。在C#中,这表现为SocketException,你必须深入了解WinSock 2 API以找出错误代码是什么。最好只指定接收的最大大小并将接收放入循环中。
我将回应另一位回应者所说的话 - 使用单一责任原则。设计一个只有一个作业的类。对于您的设计,我将从一个类开始,该类将更高级别的套接字通信封装到您的应用程序中。然后我会将该类派生为服务器类,也许是客户端类。然后,您可以在“RoomServer”和“RoomClient”类中使用这些类。这种关注点的分离应该迫使你将每个对象建模为真实世界的对象 - 谈话者和听众,它会让你思考每个人需要什么,并且需要从课程中删除与该课程的主要工作无关的无关成员变量并找到一个更好的家。