1

サーバーに接続された全二重 C# TCP クライアントをセットアップするために、いくつかの例から抜粋しました。基本的な概念は、クライアントとサーバーの両方がメッセージ (コマンドとイベント) を送受信するというものです。そのため、サーバーにメッセージを送信する Send メソッドと、サーバーからメッセージを受信するイベント ハンドラーを公開する FullDuplexSocket クラスを開発しました。サーバーから受信したメッセージのバッファをフラッシュできないように見えることを除いて、すべてが機能します。サーバーが新しいメッセージを送信するたびに、ソケット内のバッファーにはすべての古いメッセージ (既に読み取られた) が含まれ新しいメッセージ。既知の区切り文字 (/r/n) でメッセージを分割して追跡することもできますが、これは長時間の通信でメモリの問題の原因となる可能性があります。[編集: バッファの問題がなく、正しく動作するソリューションにコードを更新しました]。

誰にも提案はありますか。総書き直し??? 他の人に役立つことを願って、コードを以下に示します。

FullDuplexSocket クラスは次のとおりです。

using System;
using System.Text;
using System.Net.Sockets;

namespace Utilities
{
    public class StateObject{
        public Socket workSocket = null;
        public const int BUFFER_SIZE = 1024;
        public byte[] buffer = new byte[BUFFER_SIZE];
    }

    public class FullDuplexSocket : IDisposable
    {
        public event NewMessageHandler OnMessageReceived;
        public delegate void NewMessageHandler(string Message);
        public event DisconnectHandler OnDisconnect;
        public delegate void DisconnectHandler(string Reason);

        private Socket _socket;
        private bool _useASCII = true;
        private string _remoteServerIp = "";
        private int _port = 0;

        /// <summary>
        /// Constructer of a full duplex client socket.   The consumer should immedately define 
        /// and event handler for the OnMessageReceived event after construction has completed.
        /// </summary>
        /// <param name="RemoteServerIp">The remote Ip address of the server.</param>
        /// <param name="Port">The port that will used to transfer/receive messages to/from the remote IP.</param>
        /// <param name="UseASCII">The character type to encode/decode messages.  Defaulted to use ASCII, but setting the value to false will encode/decode messages in UNICODE.</param>
        public FullDuplexSocket(string RemoteServerIp, int Port, bool UseASCII = true)
        {
            _useASCII = UseASCII;
            _remoteServerIp = RemoteServerIp;
            _port = Port;

            try //to create the socket and connect
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.Connect(RemoteServerIp, _port);
            }
            catch (Exception e)
            {
                throw new Exception("Unable to connect to the remote Ip.", e);
            }

            try //to listen to the socket
            {
                StateObject stateObject = new StateObject();
                stateObject.workSocket = _socket;

                _socket.BeginReceive
                    (
                        stateObject.buffer, //Buffer to load in our state object
                        0, //Start at the first position in the byte array
                        StateObject.BUFFER_SIZE, //only load up to the max per read
                        0, //Set socket flags here if necessary 
                        new AsyncCallback(ReadFromSocket), //Who to call when data arrives
                        stateObject //state object to use when data arrives
                    );
            }
            catch (Exception e)
            {
                throw new Exception("Unable to start listening to the socket.", e);
            }
        }


        /// <summary>
        /// This will read the bytes from the socket, convert the bytes to a string and fire the OnMessageReceived event.
        /// If the socket is forcibly closed, the OnDisconnect event will be fired.   This happens when the other side of
        /// the socket connection on the remote Ip is no longer available.
        /// </summary>
        /// <param name="asyncResult"></param>
        public void ReadFromSocket(IAsyncResult asyncResult)
        {
            StateObject stateObject = (StateObject)asyncResult.AsyncState; //pull out the state object
            int bytesReceived = 0;

            try //to receive the message.
            {
                bytesReceived = stateObject.workSocket.EndReceive(asyncResult); 
            }
            catch (Exception e)  //Exception will occur if connection was forcibly closed.
            {
                RaiseOnDisconnect(e.Message);
                return;
            }

            if (bytesReceived > 0)
            {
                RaiseOnMessageReceived
                    (
                        _useASCII ?
                            Encoding.ASCII.GetString(stateObject.buffer, 0, bytesReceived) :
                            Encoding.Unicode.GetString(stateObject.buffer, 0, bytesReceived)
                    );

                stateObject.workSocket.BeginReceive
                    (
                        stateObject.buffer, //Buffer to load in our state object
                        0, //Start at the first position in the byte array
                        StateObject.BUFFER_SIZE, //only load up to the max per read
                        0, //Set socket flags here if necessary 
                        new AsyncCallback(ReadFromSocket), //Who to call when data arrives
                        stateObject //state object to use when data arrives
                    );

            }
            else
            {
                stateObject.workSocket.Close();
                RaiseOnDisconnect("Socket closed normally.");
                return;
            }
        }
        /// <summary>
        /// Broadcast a message to the IP/Port.  Consumer should handle any exceptions thrown by the socket.
        /// </summary>
        /// <param name="Message">The message to be sent will be encoded using the character set defined during construction.</param>
        public void Send(string Message)
        {
            //all messages are terminated with /r/n
            Message += Environment.NewLine;

            byte[] bytesToSend = _useASCII ?
                Encoding.ASCII.GetBytes(Message) :
                Encoding.Unicode.GetBytes(Message);

            int bytesSent = _socket.Send(bytesToSend);

        }

        /// <summary>
        /// Clean up the socket.
        /// </summary>
        void IDisposable.Dispose()
        {
            try
            {
                _socket.Close();
                RaiseOnDisconnect("Socket closed via Dispose method.");
            }
            catch { }
            try
            {
                _socket.Dispose();
            }
            catch { }
        }


        /// <summary>
        /// This method will gracefully raise any delegated events if they exist.
        /// </summary>
        /// <param name="Message"></param>
        private void RaiseOnMessageReceived(string Message)
        {
            try //to raise delegates
            {
                OnMessageReceived(Message);
            }
            catch { } //when none exist ignore the Object Reference Error
        }

        /// <summary>
        /// This method will gracefully raise any delegated events if they exist.
        /// </summary>
        /// <param name="Message"></param>
        private void RaiseOnDisconnect(string Message)
        {
            try //to raise delegates
            {
                OnDisconnect(Message);
            }
            catch { } //when none exist ignore the Object Reference Error
        }

    }
}

クラスの消費者は、次のことを行うだけです。

using System;

namespace Utilities
{
    public class SocketConsumer
    {
        private FullDuplexSocket _fds;

        public Consumer()
        {
            _fds = new FullDuplexSocket("192.168.1.103", 4555);

            _fds.OnMessageReceived += fds_OnMessageReceived;

            _fds.Send("Hello World!");
        }

        void fds_OnMessageReceived(string Message)
        {
            Console.WriteLine("Message: {0}", Message);
        }
    }
}

どんな助けでも素晴らしいでしょう。ありがとう!

4

1 に答える 1