【问题标题】:Handling sockets using internal classes in C#在 C# 中使用内部类处理套接字
【发布时间】:2019-01-07 14:53:58
【问题描述】:

我创建了一个类作为另一个应用程序的插件。它应该包含要在主应用程序中使用的功能。它通常工作 - 这意味着我可以处理常用功能,如计算甚至读取文件。但是我在实现套接字类时遇到了问题。我一般都知道如何使用套接字,但在这种情况下我遇到了问题。

正如您在代码中看到的,有一个内部类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),所以我不能在不发布模块处理程序的情况下给出一个最小的工作示例。

【问题讨论】:

  • 这很清楚。在OnMethodInvoke 中,您总是创建一个新的SockAttribute 对象并在构造函数完成后再次丢弃它。每个SockAttribute 对象都有自己的“处理程序”和“侦听器”字段。
  • 你能告诉我具体在哪里吗?因为我创建了两种方法,一种包括端口作为参数,另一种不带任何参数,不带参数的不应该创建新的套接字,只需监听现有的一个,所有传入的消息都应该存储在字典中以使用 @ 读取它987654326@。这是我的意图。
  • 哦(我想)我明白你的意思。您的意思是我应该在OnMethodInvoke::socket() 中创建套接字并将套接字 存储在字典中吗?如果是这样,那么我不知道如何正确地从套接字读取,因为在 OnMethodInvoke 中定义的所有“函数”都是从主机应用程序调用的,并且消息将比主机应用程序调用 readsock() 更快地到达(可能 - 也许不是)。这就是我尝试将所有消息存储在字典中的原因。
  • 看来你有某种由 OnMethodInvoke 实现的 API。你能分享一下这个 API 的描述吗?
  • 你的意思是解释还是源代码?当然我可以分享(包括 desc 和源代码),但它是一个相当大的项目,所以我不知道我是否应该把它贴在这里。我考虑过发布 Github 链接,但我认为这是不允许的。

标签: c# sockets


【解决方案1】:

虽然你的要求还是有点模糊,但我还是试试看。

首先,我建议创建一个包含 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;
}

【讨论】:

  • 非常感谢它太接近我的需要,我才意识到我必须为我的 EOF 废话找到另一种方法
猜你喜欢
  • 1970-01-01
  • 2015-07-02
  • 1970-01-01
  • 1970-01-01
  • 2019-12-22
  • 2016-02-24
  • 1970-01-01
  • 1970-01-01
  • 2018-11-20
相关资源
最近更新 更多