TCP-сокета сервера


Я был кодирования на C# несколько недель и был просто надеялся на конструктивную критику сокет сервера, я работаю на:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace NetworkCommunication
{
    public class TCPSocketServer : IDisposable
    {
        private int portNumber;
        private int connectionsLimit;
        private Socket connectionSocket;
        private List<StateObject> connectedClients = new List<StateObject>();

        public event SocketConnectedHandler ClientConnected;
        public delegate void SocketConnectedHandler(TCPSocketServer socketServer, SocketConnectArgs e);
        public event SocketMessageReceivedHandler MessageReceived;
        public delegate void SocketMessageReceivedHandler(TCPSocketServer socketServer, SocketMessageReceivedArgs e);
        public event SocketClosedHandler ClientDisconnected;
        public delegate void SocketClosedHandler(TCPSocketServer socketServer, SocketEventArgs e);

        #region Constructors
        public TCPSocketServer(int PortNumber) : this(PortNumber, 0) { }

        public TCPSocketServer(int PortNumber, int ConnectionsLimit)
        {
            this.portNumber = PortNumber;
            this.connectionsLimit = ConnectionsLimit;
            startListening();
        }
        #endregion

        #region Send Messages
        public void SendMessage(string MessageToSend, int clientID)
        {
            try
            {
                byte[] byData = System.Text.Encoding.UTF8.GetBytes(MessageToSend + "\0");

                foreach (StateObject client in connectedClients)
                {
                    if (clientID == client.id)
                    {
                        // Send message on correct client
                        if (client.socket.Connected)
                        {
                            client.socket.Send(byData);
                        }
                        break;
                    }
                }
            }
            catch (SocketException) { }
        }

        public void SendMessage(byte[] MessageToSend, int clientID)
        {
            try
            {
                foreach (StateObject client in connectedClients)
                {
                    if (clientID == client.id)
                    {
                        // Send message on correct client
                        if (client.socket.Connected)
                        {
                            client.socket.Send(MessageToSend);
                        }
                        break;
                    }
                }
            }
            catch (SocketException) { }
        }
        #endregion

        #region Connection and Listening
        private void startListening()
        {
            try
            {
                // Create listening socket
                connectionSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                connectionSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, this.portNumber);
                // Bind to local IP Address
                connectionSocket.Bind(ipLocal);
                // Start Listening
                connectionSocket.Listen(1000);
                // Creat callback to handle client connections
                connectionSocket.BeginAccept(new AsyncCallback(onClientConnect), null);
            }
            catch (SocketException) { }
        }

        private void onClientConnect(IAsyncResult asyn)
        {
            try
            {
                // Create a new StateObject to hold the connected client
                StateObject connectedClient = new StateObject();
                connectedClient.socket = connectionSocket.EndAccept(asyn);
                if (connectedClients.Count == 0)
                {
                    connectedClient.id = 1;
                }
                else
                {
                    connectedClient.id = connectedClients[connectedClients.Count - 1].id + 1;
                }
                connectedClients.Add(connectedClient);

                // Check against limit
                if (connectedClients.Count > connectionsLimit)
                {
                    // No connection event is sent so close socket silently
                    closeSocketSilent(connectedClient.id);
                    return;
                }

                // Dispatch Event
                if (ClientConnected != null)
                {
                    SocketConnectArgs args = new SocketConnectArgs();
                    args.ConnectedIP = IPAddress.Parse(((IPEndPoint)connectedClient.socket.RemoteEndPoint).Address.ToString());
                    args.clientID = connectedClient.id;
                    ClientConnected(this, args);
                }

                // Release connectionSocket to keep listening if limit is not reached
                connectionSocket.BeginAccept(new AsyncCallback(onClientConnect), null);

                // Allow connected client to receive data and designate a callback method
                connectedClient.socket.BeginReceive(connectedClient.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(onReceivedClientData), connectedClient);
            }
            catch (SocketException) { }
            catch (ObjectDisposedException) { }
        }

        private void onReceivedClientData(IAsyncResult asyn)
        {
            String content = String.Empty;
            // Receive stateobject of the client that sent data
            StateObject dataSender = (StateObject)asyn.AsyncState;

            try
            {
                // Complete aysnc receive method and read data length
                int bytesRead = dataSender.socket.EndReceive(asyn);

                if (bytesRead > 0)
                {
                    // More data could be sent so append data received so far
                    dataSender.sb.Append(Encoding.UTF8.GetString(dataSender.buffer, 0, bytesRead));
                    content = dataSender.sb.ToString();
                    if ((content.Length > 0) || (content.IndexOf("") > -1))
                    {
                        String formattedMessage = String.Empty;
                        formattedMessage += content.Replace("\0", "");

                        // Dispatch Event
                        if (MessageReceived != null)
                        {
                            SocketMessageReceivedArgs args = new SocketMessageReceivedArgs();
                            args.MessageContent = formattedMessage;
                            args.clientID = dataSender.id;
                            MessageReceived(this, args);
                        }

                        dataSender.sb.Length = 0;
                    }
                    try
                    {
                        dataSender.socket.BeginReceive(dataSender.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(this.onReceivedClientData), dataSender);
                    }
                    catch (SocketException) { }
                }
                else
                {
                    closeSocket(dataSender.id);
                }
            }
            catch (SocketException) { }
            catch (ObjectDisposedException) { }
        }
        #endregion

        #region Socket Closing
        public void closeSocket(int SocketID)
        {
            foreach (StateObject client in connectedClients.ToList())
            {
                try
                {
                    if (SocketID == client.id)
                    {
                        client.socket.Close();
                        client.socket.Dispose();

                        // Dispatch Event
                        if (ClientDisconnected != null)
                        {
                            SocketEventArgs args = new SocketEventArgs();
                            args.clientID = client.id;
                            ClientDisconnected(this, args);
                        }

                        connectedClients.Remove(client);
                        break;
                    }
                }
                catch (SocketException) { }
            }
        }

        // This does not dispatch an event, this task is to be used when rejecting connections past the limit.
        // No connection event is sent so no disconnection event should be sent.
        private void closeSocketSilent(int SocketID)
        {
            foreach (StateObject client in connectedClients.ToList())
            {
                if (SocketID == client.id)
                {
                    try
                    {
                        client.socket.Close();
                        client.socket.Dispose();
                        connectedClients.Remove(client);
                        break;
                    }
                    catch (SocketException) { }
                }
            }
        }

        public void closeAllSockets()
        {
            foreach (StateObject client in connectedClients.ToList())
            {
                closeSocket(client.id);
            }
        }
        #endregion

        public void Dispose()
        {
            this.ClientConnected = null;
            this.ClientDisconnected = null;
            this.MessageReceived = null;

            connectionSocket.Close();
        }
    }
}

Вот новая обновленная версия с помощью мне дали любезно ниже. Я добавил обработку ошибок и перешли некоторые части вокруг:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace NetworkCommunication
{
    public class TCPSocketServer : IDisposable
    {
        private const int MaxLengthOfPendingConnectionsQueue = 1000;

        private int portNumber;
        private int connectionsLimit;
        private Socket connectionSocket;
        private Dictionary<int, StateObject> connectedClients = new Dictionary<int, StateObject>();

        public event SocketConnectedHandler ClientConnected;
        public delegate void SocketConnectedHandler(TCPSocketServer socketServer, SocketConnectArgs e);
        public event SocketMessageReceivedHandler MessageReceived;
        public delegate void SocketMessageReceivedHandler(TCPSocketServer socketServer, SocketMessageReceivedArgs e);
        public event SocketClosedHandler ClientDisconnected;
        public delegate void SocketClosedHandler(TCPSocketServer socketServer, SocketEventArgs e);

        #region Constructor
        public TCPSocketServer(int PortNumber, int ConnectionsLimit = 0)
        {
            // Validate Port Number
            if (PortNumber > 0 && PortNumber < 65536)
            {
                this.portNumber = PortNumber;
            }
            else
            {
                throw new InvalidPortNumberException("Ports number must be in the 1-65535 range. Note: 256 and bellow are normally reserved.");
            }

            this.connectionsLimit = ConnectionsLimit;
            startListening();
        }
        #endregion

        private StateObject GetClient(int clientId)
        {
            StateObject client;
            if (!connectedClients.TryGetValue(clientId, out client))
            {
                return null;
            }
            return client;
        }

        #region Send Messages
        public void SendMessage(string MessageToSend, int clientID)
        {
            byte[] data = System.Text.Encoding.UTF8.GetBytes(MessageToSend + "\0");
            SendMessage(data, clientID);
        }

        public void SendMessage(byte[] MessageToSend, int clientID)
        {
            StateObject client = GetClient(clientID);
            if (client != null)
            {
                try
                {
                    if (client.socket.Connected)
                    {
                        client.socket.Send(MessageToSend);
                    }
                }
                catch (SocketException)
                {
                    // Close socket
                    closeSocket(clientID);
                }
            }
        }
        #endregion

        #region Connection and Listening
        private void startListening()
        {
            try
            {
                // Create listening socket
                connectionSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                connectionSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, this.portNumber);
                // Bind to local IP Address
                connectionSocket.Bind(ipLocal);
                // Start Listening
                connectionSocket.Listen(MaxLengthOfPendingConnectionsQueue);
                // Create callback to handle client connections
                connectionSocket.BeginAccept(new AsyncCallback(onClientConnect), null);
            }
            catch (SocketException) 
            {
                throw new SocketCannotListenException("Cannot listen on this socket. Fatal Error");
            }
        }

        private void onClientConnect(IAsyncResult asyn)
        {
            // Create a new StateObject to hold the connected client
            StateObject connectedClient = new StateObject();
            try
            {
                connectedClient.socket = connectionSocket.EndAccept(asyn);
                connectedClient.id = !connectedClients.Any() ? 1 : connectedClients.Keys.Max() + 1;

                connectedClients.Add(connectedClient.id, connectedClient);

                // Check against limit
                if (connectedClients.Count > connectionsLimit && connectionsLimit != 0)
                {
                    // No connection event is sent so close connection quietly
                    closeSocket(connectedClient.id, true);
                    return;
                }

                // Allow connected client to receive data and designate a callback method
                connectedClient.socket.BeginReceive(connectedClient.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(onReceivedClientData), connectedClient);
            }
            catch (Exception)
            {
                closeSocket(connectedClient.id, true);
            }
            finally
            {
                // Perfomed here to not get any exceptions on the main socket caught up in client connection errors.
                ReleaseConnectionSocket();
            }

            // Dispatch Event at the end as any errors in socket dispatch silent diconnections
            if (ClientConnected != null)
            {
                SocketConnectArgs args = new SocketConnectArgs()
                {
                    ConnectedIP = IPAddress.Parse(((IPEndPoint)connectedClient.socket.RemoteEndPoint).Address.ToString()),
                clientID = connectedClient.id
                };
                ClientConnected(this, args);
            }
        }

        private void ReleaseConnectionSocket()
        {
            try
            {
                // Release connectionSocket to keep listening
                connectionSocket.BeginAccept(new AsyncCallback(onClientConnect), null);
            }
            catch (SocketException)
            {
                throw new SocketCannotListenException("Cannot listen on the main socket. Fatal Error");
            }
        }

        private void onReceivedClientData(IAsyncResult asyn)
        {
            // Receive stateobject of the client that sent data
            StateObject dataSender = (StateObject)asyn.AsyncState;

            try
            {
                // Complete aysnc receive method and read data length
                int bytesRead = dataSender.socket.EndReceive(asyn);

                if (bytesRead > 0)
                {
                    // More data could be sent so append data received so far
                    dataSender.sb.Append(Encoding.UTF8.GetString(dataSender.buffer, 0, bytesRead));
                    String content = dataSender.sb.ToString();
                    if (!string.IsNullOrEmpty(content))
                    {
                        String formattedMessage = content.Replace("\0", "");

                        // Dispatch Event
                        if (MessageReceived != null)
                        {
                            SocketMessageReceivedArgs args = new SocketMessageReceivedArgs() {
                            MessageContent = formattedMessage,
                            clientID = dataSender.id
                        };
                            MessageReceived(this, args);
                        }

                        dataSender.sb.Clear();
                    }
                    try
                    {
                        dataSender.socket.BeginReceive(dataSender.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(this.onReceivedClientData), dataSender);
                    }
                    catch (SocketException) {
                        closeSocket(dataSender.id);
                    }
                }
                else
                {
                    closeSocket(dataSender.id);
                }
            }
            catch (SocketException ex) {
                // Socket closed at other end
                if (ex.ErrorCode == 10054)
                {
                    closeSocket(dataSender.id);
                }
                else
                {
                    closeSocket(dataSender.id);
                }
            }
        }
        #endregion

        #region Socket Closing
        public void closeSocket(int SocketID)
        {
            closeSocket(SocketID, false);
        }

        // This does not dispatch an event, this task is to be used when rejecting connections past the limit.
        // No connection event is sent so no disconnection event should be sent.
        private void closeSocket(int SocketId, bool silent)
        {
            StateObject client = GetClient(SocketId);
            if (client == null)
            {
                return;
            }
            try
            {
                client.socket.Close();
                client.socket.Dispose();

                if (!silent)
                {
                    // Dispatch event
                    if (ClientDisconnected != null)
                    {
                        SocketEventArgs args = new SocketEventArgs()
                        {
                            clientID = client.id
                        };
                        ClientDisconnected(this, args);
                    }
                }
            }
            catch (SocketException)
            {
                // Socket is being removed anyway.
            }
            finally
            {
                connectedClients.Remove(client.id);
            }
        }

        public void closeAllSockets()
        {
            var keys = connectedClients.Keys;
            foreach (int key in keys)
            {
                var client = connectedClients[key];
                closeSocket(client.id);
            }
        }
        #endregion

        public void Dispose()
        {
            this.ClientConnected = null;
            this.ClientDisconnected = null;
            this.MessageReceived = null;

            connectionSocket.Close();
        }
    }
}


27285
13
задан 11 октября 2011 в 04:10 Источник Поделиться
Комментарии
4 ответа

Я не буду комментировать сам функциональности TCP.
Я не компетентен для этого.

Остановка вещания - специально?

Когда вы вещания клиентам в метод SendMessage(), кажется, что вы прекратить вещание, если вы получаете проблемы, достигнув одного из клиентов. Это предназначено?
Я бы попробовать/поймать внутри foreach, и вокруг если. Затем, в ловить, я бы явно нарушать; Из foreach или Оставить комментарий, как // плевать, игнорировать и продолжить.

Пустую попробовать-ловит

Как @ChaosPandion предложил, я бы не уходить с пустыми попробовать-ловит.
Я бы либо справиться с ними, попытайтесь выполнить рефакторинг кода, а не выбрасывать их, или Оставить комментарий с краткими пояснениями.

Краткий код

Если вы используете C#4.0, я бы оставить значения по умолчанию:

    #region Constructors
// This behaves the same as the other version with two constructors.
public TCPSocketServer(int PortNumber, int ConnectionsLimit = 0)
{

Параметры - случае

Вы используете верблюжьего для локальных переменных, который соглашается с тем, что я думаю, является де-факто конвенции.
Похоже, вы используете PascalCase для параметров. Разве это не должно быть вместо верблюжьего, так же как и локальные переменные?

Венгерская нотация

Я хотел бы избежать венгерская нотация.
У меня нет реальных предложений для байт[] byData хотя. Может, просто данные? encodedData?

Дублирование кода

Дублирование кода-это рецепт для катастрофы. Вы обновить один из разделов кодекса, а не другая, и так далее...

Поэтому я бы предположил, что первый метод SendMessage просто использует второй:

public void SendMessage(string MessageToSend, int clientID)
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(MessageToSend + "\0");
SendMessage(data, clientID);
}

Магия чисел

Я бы извлечь это количество на константу где-то, и назвать его соответствующим образом. SocketListenTimeoutMillisec?

// Start Listening
connectionSocket.Listen(1000);

Черная магия

Люди получили макнули гораздо меньше, чем это. :)

content.IndexOf("") > -1

Это ничего, учитывая, что строка имеет длину > 0? Контекст:

content = dataSender.sb.ToString();
if ((content.Length > 0) || (content.IndexOf("") > -1))

Код Succint

String formattedMessage = String.Empty;
formattedMessage += content.Replace("\0", "");

может быть

String formattedMessage = content.Replace("\0", "");

ВАР деклараций ближе к использованию

String content = String.Empty;

мог двигаться рядом с контентом = dataSender.СБ.Метод toString();, и даже быть интегрированы в этой строке:

string content = dataSender.sb.ToString();

Использование класса StringBuilder лучше

// instead of dataSender.sb.Length = 0;
dataSender.sb.Clear();

бесполезно .Список()

    foreach (StateObject client in connectedClients.ToList())

LINQ, которая делает succint код

foreach (StateObject client in connectedClients.ToList()) {
if (SocketID == client.id) {

можно заменить

StateObject client = connectedClients.Where( conClient => conClient.id == SocketID );
if( client != null ) {

Конструктор объекта

// Parentheses are optional, if empty.
SocketMessageReceivedArgs args = new SocketMessageReceivedArgs() {
MessageContent = formattedMessage,
clientID = dataSender.id
};

И же для других аналогичных заданий собственность сразу после соответствующего конструктора.


Мое предложение более чистого кода

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace NetworkCommunication
{
class StateObject { }
class SocketConnectArgs { }
class SocketMessageReceivedArgs { }
class SocketEventArgs { }

public class TCPSocketServer : IDisposable
{
// Or whatever the name.
const int MaxLengthOfPendingConnectionsQueue = 1000;

private int portNumber;
private int connectionsLimit;
private Socket connectionSocket;
private Dictionary<int, StateObject> connectedClients = new Dictionary<int, StateObject>();

public event SocketConnectedHandler ClientConnected;
public delegate void SocketConnectedHandler(TCPSocketServer socketServer, SocketConnectArgs e);
public event SocketMessageReceivedHandler MessageReceived;
public delegate void SocketMessageReceivedHandler(TCPSocketServer socketServer, SocketMessageReceivedArgs e);
public event SocketClosedHandler ClientDisconnected;
public delegate void SocketClosedHandler(TCPSocketServer socketServer, SocketEventArgs e);

#region Constructors
public TCPSocketServer(int portNumber, int connectionsLimit = 0) {
this.portNumber = portNumber;
this.connectionsLimit = connectionsLimit;
startListening();
}
#endregion

private StateObject GetClient(int clientId) {
StateObject client;
if(!connectedClients.TryGetValue(clientId, out client)) {
return null;
}
return client;
}

#region Send Messages
public void SendMessage(string messageToSend, int clientID) {
byte[] data = System.Text.Encoding.UTF8.GetBytes(messageToSend + "\0");
SendMessage(data, clientID);
}

public void SendMessage(byte[] messageToSend, int clientID) {
StateObject client = GetClient(clientID);
if (client != null) {
try {
if (client.socket.Connected) {
client.socket.Send(messageToSend);
}
} catch (SocketException) {
// TODO: sending failed; disconnect from client, or?
}
}
}
#endregion

#region Connection and Listening
private void startListening() {
try {
// Create listening socket
connectionSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
connectionSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, this.portNumber);
// Bind to local IP Address
connectionSocket.Bind(ipLocal);
// Start Listening
connectionSocket.Listen(MaxLengthOfPendingConnectionsQueue);
// Creat callback to handle client connections
connectionSocket.BeginAccept(new AsyncCallback(onClientConnect), null);
} catch (SocketException) {
// TODO: if we fail to start listening, is it even ok to continue?
// Consider that some of the bootstrapping actions might not even have been done.
// Thus execution will likely crash on next step.
}
}

private void onClientConnect(IAsyncResult asyn) {
try {
// Create a new StateObject to hold the connected client
StateObject connectedClient = new StateObject() {
socket = connectionSocket.EndAccept(asyn),
id = !connectedClients.Any() ? 1 : connectedClients.Keys.Max() + 1
};

connectedClients.Add(connectedClient);

// TODO: consider if we can instead do this at the beginning of the method.
// Check against limit
if (connectedClients.Count > connectionsLimit) {
// No connection event is sent so close socket silently
closeSocket(connectedClient.id, true);
return;
}

// Dispatch Event
if (ClientConnected != null) {
SocketConnectArgs args = new SocketConnectArgs() {
ConnectedIP = IPAddress.Parse(((IPEndPoint)connectedClient.socket.RemoteEndPoint).Address.ToString()),
clientID = connectedClient.id
};
ClientConnected(this, args);
}

// Release connectionSocket to keep listening if limit is not reached
connectionSocket.BeginAccept(new AsyncCallback(onClientConnect), null);

// Allow connected client to receive data and designate a callback method
connectedClient.socket.BeginReceive(connectedClient.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(onReceivedClientData), connectedClient);
} catch (SocketException) {
// TODO: should we closeSocketSilent()? Or?
} catch (ObjectDisposedException) {
// TODO: should we closeSocketSilent()? Or?
}
}

private void onReceivedClientData(IAsyncResult asyn) {
// Receive stateobject of the client that sent data
StateObject dataSender = (StateObject)asyn.AsyncState;

try {
// Complete aysnc receive method and read data length
int bytesRead = dataSender.socket.EndReceive(asyn);

if (bytesRead > 0) {
// More data could be sent so append data received so far
dataSender.sb.Append(Encoding.UTF8.GetString(dataSender.buffer, 0, bytesRead));
if ( dataSender.sb.Length != 0
&& MessageReceived != null
) {
// TODO: is it possible that multiple messages are in the sb?
// Consider whether it's necessary to replace with newline.
dataSender.sb.Replace("\0", null); // Removes them.

// Dispatch Event
SocketMessageReceivedArgs args = new SocketMessageReceivedArgs();
args.MessageContent = dataSender.sb.ToString();
args.clientID = dataSender.id;
MessageReceived(this, args);

dataSender.sb.Clear();
}
try {
dataSender.socket.BeginReceive(dataSender.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(this.onReceivedClientData), dataSender);
} catch (SocketException) { }
} else {
closeSocket(dataSender.id);
}
} catch (SocketException) {
// TODO: should we closeSocketSilent()? Or?
} catch (ObjectDisposedException) {
// TODO: should we closeSocketSilent()? Or?
}
}
#endregion

#region Socket Closing
public void closeSocket(int socketID) {
closeSocket(socketID, false);
}

/// <param name="silent">Whether to skip dispatching the disconnection event. Used to cancel the bootstrapping of the client-server connection.</param>
private void closeSocket(int socketID, bool silent) {
StateObject client = GetClient(socketID);
if (client == null) {
return;
}
try {
client.socket.Close();
client.socket.Dispose();

if(!silent) {
// Dispatch Event
if (ClientDisconnected != null) {
SocketEventArgs args = new SocketEventArgs();
args.clientID = client.id;
ClientDisconnected(this, args);
}
}
// Moved to finnaly block: connectedClients.Remove(client.id);
} catch (SocketException) {
// Don't care. Or?
} finally {
connectedClients.Remove(client.id);
}
}

public void closeAllSockets() {
var keys = connectedClients.Keys;
foreach( int key in keys ) {
var client = connectedClients[key];
closeSocket(client.id);
}
}
#endregion

public void Dispose() {
ClientConnected = null;
ClientDisconnected = null;
MessageReceived = null;

connectionSocket.Close();
}
}
}

ЗЫ: ты мне должен "пиво". :)
(Это было весело, хотя!)

редактировать: верблюжьего параметров; уменьшено дублирование кода; словарь вместо списка; больше конструкторов объектов.
edit2: класса StringBuilder методов как предложил @pstrjds.

13
ответ дан 8 ноября 2011 в 04:11 Источник Поделиться

Обработка событий является немного нестандартным. А не:

public event SocketConnectedHandler ClientConnected;
public delegate void SocketConnectedHandler(TCPSocketServer socketServer, SocketConnectArgs e);

это было бы более привычно (и, следовательно, менее удивительно) иметь:

public event EventHandler<SocketConnectArgs> ClientConnected;

Недостатком более обычный подход, конечно, является то, что вы должны бросить объект отправителя, к TCPSocketServer, так что есть компромисс.


Как вы используете connectedClients указывает на то, что у вас типа неправильно. В почти всех доступов вы итерации по проверке элементов, чтобы увидеть, является ли их код соответствует значению. Что говорит о том, что это должен быть класс IDictionaryили что вы должны пройти StateObject вокруг, а не идентификатор. Вам также может понадобиться, чтобы синхронизировать доступ к нему - я не прибито, может ли он получить доступ более чем одним потоком.

6
ответ дан 8 ноября 2011 в 01:11 Источник Поделиться

Мое самое большое беспокойство-это ваше чрезмерное использование пустых попробовать заявлений. Вы должны обрабатывать любые исключения socketexception , который может быть брошен, или позволить им распространяться. Как она стоит я бы не рассматривала этот готовый рабочий код.

3
ответ дан 11 октября 2011 в 04:10 Источник Поделиться

Я только сейчас заметил этот вопрос пару недель, но тем не менее я добавлю свои пять копеек.
Я бы ваш метод SendMessage перегрузку, которая принимает строку выполнить преобразование байт, что он делает, но вместо того, чтобы каждый метод SendMessage дублирующего кода, который ходит список, ищу обрыв связи, я хотел бы иметь метод SendMessage со строкой, просто позвоните в метод SendMessage , которая принимает массив Byte[]

public void SendMessage(string MessageToSend, int clientID)
{
byte[] byData = System.Text.Encoding.UTF8.GetBytes(MessageToSend + "\0");
SendMessage(byData, clientID);
}

public void SendMessage(byte[] MessageToSend, int clientID)
{
try
{
foreach (StateObject client in connectedClients)
{
if (clientID == client.id)
{
// Send message on correct client
if (client.socket.Connected)
{
client.socket.Send(MessageToSend);
}
break;
}
}
}
catch (SocketException) { }
}

Код кажется немного странным, когда новые подключения идут. Если вы исчерпали лимит подключений, вы собираетесь обрабатывать закрыть сокет, который вы создали и просто вернуть. Похоже, вы могли бы сначала проверить лимит и вернуться, не выполняя никакой работы, чтобы создать объект состояния, добавить в список, то проверяем лимит и удалить из списка.

И наконец, подумайте, не перебирая всех подключенных клиентов в каждый метод, который принимает параметр ClientID. Вы могли бы сделать ваш контейнер словарь так что вы можете просто использовать ClientID таким образом, чтобы получить объект без прохода по списку.

3
ответ дан 8 ноября 2011 в 02:11 Источник Поделиться