Обработка сокетов с использованием внутренних классов в C # - PullRequest
0 голосов
/ 06 января 2019

Я создал класс, который действует как плагин для другого приложения. Он должен содержать функции для использования в основном приложении. В целом это работает - это означает, что я могу обрабатывать обычные функции, такие как вычисления и даже чтение файлов. Но у меня есть проблемы с реализацией класса сокетов. Я знаю, как работать с сокетами в целом, но в этом случае у меня проблема.

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

public class Net : Module {

    private static ReadOnlyCollection<CustomMethod> customMethods;

    internal class SockAttrib {

        public Socket listener;
        public Socket handler;

        /* create the listener */
        public SockAttrib(int port) {
            IPHostEntry host = Dns.GetHostEntry("localhost");
            IPAddress ipAddress = host.AddressList[1];
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);          
            try {
                listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                listener.Bind(localEndPoint);
                listener.Listen(10);
                handler = listener.Accept();
            } catch (Exception e) { Console.WriteLine("socket() " + e); }
        }

        /* listen */
        public SockAttrib() {
                try {
                // Incoming data from the client.    
                string data = "";
                byte[] bytes = null;
                    while (true) {
                        bytes = new byte[1024];
                        int bytesRec = handler.Receive(bytes);
                        data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                        if (data.IndexOf("<EOF>") > -1)
                        {
                            messages[++idm] = data;
                            //return;
                        }
                    }
                }
                catch (Exception e) { Console.WriteLine("listen() "+e); }
        }

        public void Close() {
            handler.Close();
        }
    }

    /* message index */
    private static int idm = 0;
    /* list of messages */
    private static Dictionary<int, String> messages = new Dictionary<int, String>();

    public Net() {               
        if (customMethods != null) return;
        List<CustomMethod> moduleMethods = new List<CustomMethod>();
        moduleMethods.Add(new CustomMethod(typeof(int), "socket", typeof(int)));
        moduleMethods.Add(new CustomMethod(typeof(int), "listen" ));
        moduleMethods.Add(new CustomMethod(typeof(string), "sockread"));
        customMethods = moduleMethods.AsReadOnly();
    }

    public ReadOnlyCollection<CustomMethod> Prototypes {
        get { return customMethods; }
    }

    public object OnMethodInvoke(String functionName, List<object> parameters) {

        if( functionName == "socket") {
            int port = (int)parameters[0];
            SockAttrib sa = new SockAttrib( port );
            return 1;
        }

        if (functionName == "listen") {
            SockAttrib sa = new SockAttrib();
            return 1; 
        }

        if (functionName == "sockread") {
            if (idm > 0) {
                String message = messages[--idm];
                return message;
            } else {
                return "nope";
            }
        }

        return false;
    }

}

Моя проблема в обработчике. Создание сокета работает, но как только я подключаюсь к сокету, используя netcat, сокет перестает слушать, и я не получаю никаких ответов. Я надеюсь, что это не слишком много кода, и он также должен быть легко читаемым.

Наконец, модуль экспортируется в виде библиотеки (dll), поэтому я не могу привести минимальный рабочий пример без публикации обработчика модуля.

1 Ответ

0 голосов
/ 07 января 2019

Хотя ваши требования все еще немного размыты, я попробую.

Сначала я бы порекомендовал создать класс, содержащий основные функции вашего TCP-сервера. Это облегчает юнит-тестирование вашего кода и адаптацию его к меняющимся требованиям.

/// <summary>
/// This class encapsulates the TCP server
/// </summary>
public class Server : IDisposable
{
    private static TcpListener _listener;
    private static TcpClient _client;
    private static NetworkStream _stream;
    private static byte[] _buffer;
    private static readonly StringBuilder _receivedText = new StringBuilder();
    private const string EOF = "<EOF>";

    /// <summary>
    /// Starts listening on the specified port
    /// </summary>
    /// <param name="port">The port number</param>
    public Server(int port)
    {
        _listener = new TcpListener(IPAddress.Any, port);
        _listener.Start();
        _listener.BeginAcceptTcpClient(Accepted, null);
    }

    public void Dispose()
    {
        if (_client != null)
        {
            _client.Dispose();
        }
        if (_listener != null)
        {
            _listener.Stop();
        }
    }

    /// <summary>
    /// Returns any text that has been sent via TCP to the port specified in the constructor.
    /// </summary>
    /// <returns>The received text, or null if no (complete) text has been received yet.</returns>
    /// <remarks>
    /// The method returns the next text delimited by "&lt;EOF&gt;".
    /// </remarks>
    public string Read()
    {
        lock (_receivedText)
        {
            var receivedText = _receivedText.ToString();
            var eofIndex = receivedText.IndexOf(EOF);
            if (eofIndex < 0)
                return null; // no (complete) text has been received yet
            var result = receivedText.Substring(0, eofIndex);
            _receivedText.Remove(0, eofIndex + EOF.Length);
            return result;
        }
    }

    // called whenever a client has connected to our server.
    private static void Accepted(IAsyncResult ar)
    {
        _client = _listener.EndAcceptTcpClient(ar);
        _stream = _client.GetStream();
        _buffer = new byte[4096];
        _stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
    }

    // called whenever data has arrived or if the client closed the TCP connection
    private static void Read(IAsyncResult ar)
    {
        var bytesReceived = _stream.EndRead(ar);
        if (bytesReceived == 0)
        {
            // TCP connection closed
            _client.Close();
            _client = null;
            _stream.Dispose();
            _stream = null;
            // prepare for accepting the next TCP connection
            _listener.BeginAcceptTcpClient(Accepted, null);
            return;
        }

        lock (_receivedText)
        {
            _receivedText.Append(Encoding.ASCII.GetString(_buffer, 0, bytesReceived));
        }

        // prepare for reading more
        _stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
    }
}

Интеграция этого в ваш Net класс должна быть довольно простой:

// static or not? Depends on your "Module plugin" architecture
private static Server _server;

public object OnMethodInvoke(String functionName, List<object> parameters)
{
    if (functionName == "socket")
    {
        if (_server != null)
        {
            // oops, already open
            return 0;
        }
        int port = (int)parameters[0];
        _server = new Server(port);
        return 1;
    }

    if (functionName == "sockread")
    {
        if (_server != null)
        {
            return _server.Read() ?? "nope";
        }
        else
        {
            return "nope";
        }
    }

    return false;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...