簡體   English   中英

在C#中使用內部類處理套接字

[英]Handling sockets using internal classes in C#

我創建了一個充當另一個應用程序插件的類。 它應該擁有要在主應用程序中使用的功能。 它通常可以正常工作-這意味着我可以處理常用功能,例如計算甚至讀取文件。 但是我在實現套接字類時遇到問題。 我知道一般如何使用套接字,但是在這種情況下我有問題。

您可能會在代碼中看到,有一個內部類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),因此我不能在不發布模塊處理程序的情況下給出一個最小的工作示例。

盡管您的要求仍然有些模糊,但我會嘗試一下。

首先,我建議創建一個包含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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM