C # Асинхронные сокеты / многопоточность: правильно ли я использую асинхронные сокеты и многопоточность? - PullRequest
0 голосов
/ 18 мая 2011

Я прочитал множество уроков и других страниц по асинхронным сокетам в C #. Мне нужно написать TCP-клиент, который может выполнять чтение и запись одновременно. Вот почему я выбрал асинхронные сокеты. Вот код Я правильно использую асинхронные сокеты и многопоточность?

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

    namespace WindowsFormsApplication2
    {
        public delegate void ConnectEventHandler(object sender, ConnectEventArgs e);
        // More similar delegates defined for each event to pass to the GUI thread...

        public class Client
        {
            bool m_MasterSwitch;
            ArrayList m_ProductList;

            public event ConnectEventHandler ConnectEvent = delegate { };

            Socket m_Socket;

            EventWaitHandle m_WaitHandle;
            readonly object m_Locker;
            Queue<IEvent> m_Tasks;
            Thread m_Thread;

            public Client()
            {
                m_MasterSwitch = false;
                m_ProductList = new ArrayList();

                m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                m_WaitHandle = new AutoResetEvent(false);
                m_Locker = new object();
                m_Tasks = new Queue<IEvent>();

                m_Thread = new Thread(Run);
                m_Thread.IsBackground = true;
                m_Thread.Start();
            }

            public void EnqueueTask(IEvent task)
            {
                lock (m_Locker)
                {
                    m_Tasks.Enqueue(task);
                }

                m_WaitHandle.Set();
            }

            private void Run()
            {
                while (true)
                {
                    m_WaitHandle.WaitOne();
                    IEvent task = null;

                    lock (m_Locker)
                    {
                        if (m_Tasks.Count == 0)
                        {
                            m_WaitHandle.Reset();
                            continue;
                        }

                        task = m_Tasks.Dequeue();
                    }

                    if (task == null)
                    {
                        return;
                    }
                    else
                    {
                        task.DoTask(this);
                    }
                }
            }

            public void Connect(string hostname, int port)
            {
                try
                {
                    IPAddress[] IPs = Dns.GetHostAddresses(hostname);

                    m_Socket.BeginConnect(IPs, port, new AsyncCallback(ConnectCallback), m_Socket);
                }
                catch (Exception)
                {
                    OnConnect(false, "Unable to connect to server.");
                }
            }

            private void ConnectCallback(IAsyncResult ar)
            {
                try
                {
                    Socket socket = (Socket)ar.AsyncState;

                    socket.EndConnect(ar);

                    OnConnect(true, "Successfully connected to server.");

                    StartRead(socket);
                }
                catch (Exception)
                {
                    OnConnect(false, "Unable to connect to server.");
                }
            }

            private void StartRead(Socket socket)
            {
                StateObject state = new StateObject();
                state.AsyncSocket = socket;

                socket.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
            }

            private void ReceiveCallback(IAsyncResult ar)
            {
                try
                {
                    StateObject state = (StateObject)ar.AsyncState;

                    int bytes_read = state.AsyncSocket.EndReceive(ar);

                    char[] chars = new char[bytes_read + 1];
                    System.Text.Decoder decoder = System.Text.Encoding.UTF8.GetDecoder();
                    int charLength = decoder.GetChars(state.Buffer, 0, bytes_read, chars, 0);

                    String data = new String(chars);

                    ParseMessage(data);

                    StartRead(state.AsyncSocket);
                }
                catch (ObjectDisposedException)
                {
                }
                catch (SocketException)
                {
                }
            }

            private void StartSend(string message)
            {
                try
                {
                    StateObject state = new StateObject();
                    state.AsyncSocket = m_Socket;

                    byte[] buffer = Encoding.UTF8.GetBytes(message);
                    m_Socket.BeginSend(buffer, 0, buffer.Length, 0, new AsyncCallback(SendCallback), state);
                }
                catch (Exception)
                {
                }
            }

            private void SendCallback(IAsyncResult ar)
            {
                try
                {
                    StateObject state = (StateObject)ar.AsyncState;
                    state.AsyncSocket.EndSend(ar);
                }
                catch
                {
                }
            }

            private void ParseMessage(string data)
            {
                string[] messages = data.Split('\n');

                foreach (string message in messages)
                {
                    if (message != "\0")
                    {
                        string[] tokens = message.Split(',');

                        string command = tokens[0];

                        switch (command)
                        {
                            case "@1":
                            {
                                string exchangeName = tokens[1];
                                string connectionStatus = tokens[2];
                                string loggedInStatus = tokens[3];

                                bool connectionStatusBool;
                                bool loggedInStatusBool;

                                if (connectionStatus == "1")
                                {
                                    connectionStatusBool = true;
                                }
                                else
                                {
                                    connectionStatusBool = false;
                                }

                                if (loggedInStatus == "1")
                                {
                                    loggedInStatusBool = true;
                                }
                                else
                                {
                                    loggedInStatusBool = false;
                                }

                                OnExchangeStatusMessage(exchangeName, connectionStatusBool, loggedInStatusBool);

                                break;
                            }
                            case "@2":
                            {
                                string isError = tokens[1];
                                string logMessage = tokens[2];
                                bool isErrorBool;

                                if (isError == "error")
                                {
                                    isErrorBool = true;
                                }
                                else
                                {
                                    isErrorBool = false;
                                }

                                OnLogMessage(isErrorBool, logMessage);

                                break;
                            }
                            case "@3":
                            {
                                OnLogMessage(false, message);

                                break;
                            }
                            case "@4":
                            {
                                string masterSwitch = tokens[1];

                                if (masterSwitch == "1")
                                {
                                    m_MasterSwitch = true;
                                }
                                else
                                {
                                    m_MasterSwitch = false;
                                }

                                m_ProductList.Clear();

                                for (int i = 2; i < tokens.Length; ++i)
                                {
                                    string[] productArray = tokens[i].Split('=');

                                    if (productArray.Length < 2)
                                    {
                                        break;
                                    }

                                    string name = productArray[0];
                                    string isLive = productArray[1];
                                    bool isLiveBool;

                                    if (isLive == "1")
                                    {
                                        isLiveBool = true;
                                    }
                                    else
                                    {
                                        isLiveBool = false;
                                    }

                                    m_ProductList.Add(new Product(name, isLiveBool));
                                }

                                OnLiveMessage(m_MasterSwitch, m_ProductList);

                                break;
                            }
                            default:
                                break;
                        }
                    }
                }
            }

            public void SetLiveStatus()
            {
                bool newState = (! m_MasterSwitch);
                string newStateString;

                if (newState)
                {
                    newStateString = "1";
                }
                else
                {
                    newStateString = "0";
                }

                StartSend(String.Format("@4,{0}\n", newStateString));
            }

            public void SetProductLiveStatus(Product product)
            {
                if (m_ProductList.Contains(product))
                {
                    string masterSwitchString;

                    if (m_MasterSwitch)
                    {
                        masterSwitchString = "1";
                    }
                    else
                    {
                        masterSwitchString = "0";
                    }

                    bool newState = (! product.IsLive);
                    string newStateString;

                    if (newState)
                    {
                        newStateString = "1";
                    }
                    else
                    {
                        newStateString = "0";
                    }

                    StartSend(String.Format("@4,{0},{1}={2}\n", masterSwitchString, product.Name, newStateString));
                }
            }

            private void OnConnect(bool isConnected, string message)
            {
                ConnectEvent(this, new ConnectEventArgs(isConnected, message));
            }

            // More events to pass to the GUI...
        }
    }

Ответы [ 2 ]

3 голосов
/ 18 мая 2011

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

  1. Вам не хватает фрейм сообщения .
  2. Вы игнорируете возвращаемое значение EndSend. Возможно, что частичная отправка завершена, и вам придется отправить остальные данные.
0 голосов
/ 18 мая 2011

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

Так я думаю, что он будет работать?Я не знаю.Это работает?Вы управляли этим?Если я скажу, что все в порядке, и это не работает, тогда я буду чувствовать себя глупо, потому что, возможно, я упустил что-то очевидное в вашем коде.

Но для простого обзора кода "только для глаз" я могу сказать, да,Похоже, вы используете API-интерфейс сокетов aysnc в тех рамках, для которых он был предназначен.Есть много стилистических вещей, которые я бы сделал по-другому, но это не ваш вопрос.

Но даже если у вас есть хороший базовый асинхронный дизайн, многие вещи могут пойти не так в реализации. Этот клиент / сервер все еще должен быть интегрирован в приложение, и многие проблемы асинхронного проектирования критически зависят от этих отношений.

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

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