none
關於用SocketAsyncEventArgs編寫客戶端(.net 3.5) RRS feed

  • 問題

  • 網絡上有很多教程關於以SocketAsyncEventArgs編寫客戶端,但大部份也是由客戶端主動發送及收取,如由伺服器主動發出則無法處理,編碼大概如下:

    socket.SendAsync(socketAsyncEventArgs);
    autoResetEvent.WaitOne()-> OnSend -> autoResetEvent.Set()-> socketAsyncEventArgs.Completed += OnReceive -> autoResetEvent.Set()-> handleMessage(socketAsyncEventArgs.Buffer)

    我知道任何時候也應該要調用socket.ReceiveAsync(socketAsyncEventArgs)來接收伺服器的訊息,但調用了則不用使用socket.SendAsync(socketAsyncEventArgs)  (InvalidOperationException),我是否應該用兩個執行緒分別處理Send及Receive呢? 我沒法現有方法可停用socket.ReceiveAsync

    這問題我爬了幾天文章也沒答案

    2011年6月28日 上午 11:42

解答

  • 在OnReceive的函式最後要再呼叫 BeginReceive(), 不然當然它只會讀一次. 因為它等待Receive的程序已經結束了.

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2011年6月29日 上午 09:13
    版主

所有回覆

  • 伺服器發出要用 UDP 吧,除非你知道用戶端的 IP,而且用戶端也有 Listen 你指定的 port 才可以。
    小朱的技術隨手寫:http://www.dotblogs.com.tw/regionbbs/
    雲端學堂Facebook: http://www.facebook.com/pages/StudyAzurecom-yun-duan-xue-tang/155855707799579
    2011年6月28日 下午 12:31
    版主
  • 看的不是很懂, 所以來猜猜看吧.

    基本上只要BeginConnect的Call back函式中在EndConnect方法之後直接BeginReceive就好了.

    這樣一旦Client Connect 上去, 只要不中斷連結, 就會保持receive的狀態, 然後你的 BeginSend還是照送, 這沒有差的.

    因為在非同步狀態下基本上就和多緒是一樣的.

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2011年6月28日 下午 12:42
    版主
  • 伺服器發出要用 UDP 吧,除非你知道用戶端的 IP,而且用戶端也有 Listen 你指定的 port 才可以。
    小朱的技術隨手寫:http://www.dotblogs.com.tw/regionbbs/
    雲端學堂Facebook: http://www.facebook.com/pages/StudyAzurecom-yun-duan-xue-tang/155855707799579
    不是用UDP的,我的用戶端已經和伺服器連接了
    2011年6月28日 下午 01:44
  • 看的不是很懂, 所以來猜猜看吧.

    基本上只要BeginConnect的Call back函式中在EndConnect方法之後直接BeginReceive就好了.

    這樣一旦Client Connect 上去, 只要不中斷連結, 就會保持receive的狀態, 然後你的 BeginSend還是照送, 這沒有差的.

    因為在非同步狀態下基本上就和多緒是一樣的.

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    這種方法好像是.net 2.0的(有錯請指正),我的伺服器是用這種做法沒問題,但用了網上提供的方法寫用戶端卻不行 :

    http://www.codeproject.com/KB/IP/socketasynceventargssampl.aspx

    上述的用戶端只能在發送後接收訊息,直接收取就是不行

    2011年6月28日 下午 01:59
  • hi,

     

                     所以,是直接呼叫Receive? 有先判斷Buffer裡有沒有資料嗎?

          http://msdn.microsoft.com/zh-tw/library/system.net.sockets.socket.available.aspx


    MVP 2010 - Visual Developer ASP/ASP.NET My Blog: http://www.dotblogs.com.tw/code6421/
    2011年6月28日 下午 03:48
  • 這種方法好像是.net 2.0的(有錯請指正),我的伺服器是用這種做法沒問題,但用了網上提供的方法寫用戶端卻不行 :

    http://www.codeproject.com/KB/IP/socketasynceventargssampl.aspx

    上述的用戶端只能在發送後接收訊息,直接收取就是不行

    Socket從 .Net 2.0 到 4.0應該是沒什麼大改變才是
    順帶一問, 你問題的重點倒底是 ?
    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2011年6月29日 上午 03:39
    版主
  • socket.SendAsync(socketAsyncEventArgs)  (InvalidOperationException)
    是說你在socket.ReceiveAsync之後呼叫socket.SendAsync發生錯誤?

    能不能把有問題的Code貼上來?比較好理解問題?


    學無止境
    2011年6月29日 上午 03:50
  • 我把部份Code貼出來吧,再說說我的問題(這個code自己也知道是錯的)

           public SocketClient(string ip, int port)
            {
                try
                {

                    IPAddress ipAddress = IPAddress.Parse(ip);         
                    hostEndPoint = new IPEndPoint(ipAddress, port);
                    clientSocket = new Socket(hostEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                }
                catch (Exception e)
                {
                    MessageReceived("#20001 " + e.Message);
                }
            }

            public void Connect()
            {
                SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();

                connectArgs.UserToken = clientSocket;
                connectArgs.RemoteEndPoint = hostEndPoint;
                connectArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnIO);
                clientSocket.ConnectAsync(connectArgs);
                clientConnected.WaitOne();   
            
                SocketError errorCode = connectArgs.SocketError;
                try
                {
                    if (errorCode != SocketError.Success)
                    {

                        throw new SocketException((Int32)errorCode);

                    }
                    else
                    {

                        MessageReceived("Socket connected to" + clientSocket.RemoteEndPoint.ToString());
                        xmlReader = new XMLMessageReader(this);
                        login();

                    }
                }
                catch (SocketException e)
                {
                    MessageReceived("#20002 " + e.Message);
                }
                catch (Exception e)
                {
                    MessageReceived("#20003 " + e.Message);
                }

     

            }

            public void Disconnect()
            {
                clientSocket.Disconnect(false);
                MessageReceived("Socket Disconnected");
            }


            private void OnConnect(object sender, SocketAsyncEventArgs e)
            {

                clientConnected.Set();
                connected = (e.SocketError == SocketError.Success);
            }


            private void StartReceive(SocketAsyncEventArgs e)
            {
                if (e == null)
                {
                    e = new SocketAsyncEventArgs();
                    e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
                }
                else
                {
                    e.AcceptSocket = null;
                }

            }

            private SocketAsyncEventArgs e2 = null;


            private void OnReceive(object sender, SocketAsyncEventArgs e)
            {
                var socket = e.UserToken as Socket;
                MessageReceived("Message Receive by OnReceive");
                handleMessage(e.Buffer);

                e.Dispose(); //以下肯定是錯的

                if (e2!=null)
                    e2.Dispose();
                e2 = new SocketAsyncEventArgs();
                e2.UserToken = clientSocket;
              
                e2.Completed += new EventHandler<SocketAsyncEventArgs>(OnIO);
                Socket s = e2.UserToken as Socket;
                e2.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
                try
                {
                    bool willRaiseEvent = s.ReceiveAsync(e2);
                    if (!willRaiseEvent)
                        OnReceive(this, e2);
                }
                catch (Exception ex)
                {
                    MessageReceived("#20007 " + ex.Message);
                }

            }

     


            // Calback for send operation
            private void OnSend(object sender, SocketAsyncEventArgs e)
            {

                // Signals the end of send.
                //autoSendReceiveEvents[(int)SocketState.ReceiveOperation].Set();
                clientDataSent.Set();

                if (e.SocketError == SocketError.Success)
                {
                    if (e.LastOperation == SocketAsyncOperation.Send)
                    {
                     
                        Socket s = e.UserToken as Socket;

                        e.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
                        bool willRaiseEvent = s.ReceiveAsync(e);
                        if (!willRaiseEvent)
                            OnReceive(this, e);

                    }
                }
                else
                {
                    ProcessError(e);
                }

            }

            private void ProcessError(SocketAsyncEventArgs e)
            {
                Socket s = e.UserToken as Socket;
                if (s.Connected)
                {
                    // close the socket associated with the client
                    try
                    {
                        s.Shutdown(SocketShutdown.Both);
                    }
                    catch (Exception er)
                    {
                        // throws if client process has already closed
                        MessageReceived("#20004" + er.Message);
                    }
                    finally
                    {
                        if (s.Connected)
                        {
                            s.Close();
                        }
                    }
                }

                // Throw the SocketException
                throw new SocketException((Int32)e.SocketError);
            }

     

            // Listen push message from host
            private void OnIO(object sender, SocketAsyncEventArgs e)
            {
                switch (e.LastOperation)
                {
                    case SocketAsyncOperation.Connect:
                        OnConnect(this, e);
                        break;
                    case SocketAsyncOperation.Receive:
                        OnReceive(this, e);
                        break;
                    case SocketAsyncOperation.Send:
                        OnSend(this, e);
                        break;
                    default:
                        throw new ArgumentException("The last operation completed on the socket was not a receive or send");
                }

            }

     

            public void SendData(String data)
            {
         
                SocketAsyncEventArgs completeArgs = null;
                    completeArgs = new SocketAsyncEventArgs();
                completeArgs.UserToken = clientSocket;
                completeArgs.RemoteEndPoint = hostEndPoint;
                completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnIO);
              

                byte[] packetTosSendCompressed = PacketHandler.SendCompressedPackage(data);
                completeArgs.SetBuffer(packetTosSendCompressed, 0, packetTosSendCompressed.Length);          
                bool willRaiseEvent = clientSocket.SendAsync(completeArgs);

                if (!willRaiseEvent)
                    OnSend(this, completeArgs);
                clientDataSent.WaitOne();
                MessageReceived("From local : " + data);
            }

     

    我知道要持續接收伺服器的話,必須在每個OnRecieve後再用ReceiveAsync。上述Code如調用SendData後可以順利傳送並接收回覆,但接收由伺服器自己Push的訊息時會無限loop (我也知道是錯了,只時急著回帖沒有再整理)

    2011年6月29日 上午 06:38
  • 我剛嘗試改了,暫試可以收到伺服器一個訊息沒Error,但不代表正確

    if (e2 == null)
                {
                   
                    e2 = new SocketAsyncEventArgs();
                    e2.UserToken = clientSocket;

                    e2.Completed += new EventHandler<SocketAsyncEventArgs>(OnIO);
                    Socket s = e2.UserToken as Socket;
                    e2.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
                    try
                    {
                        bool willRaiseEvent = s.ReceiveAsync(e2);
                        if (!willRaiseEvent)
                            OnReceive(this, e2);
                    }

                    catch (Exception ex)
                    {
                        MessageReceived("#20007 " + ex.Message);
                    }

                }

    2011年6月29日 上午 06:46
  • 試了一會,上述方法只能收到push一次
    2011年6月29日 上午 08:04
  • 在OnReceive的函式最後要再呼叫 BeginReceive(), 不然當然它只會讀一次. 因為它等待Receive的程序已經結束了.

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2011年6月29日 上午 09:13
    版主
  • 我解決問題了!關鍵是別理會網上提供的方法,大部份也是誤導的,在Send跟Receive應該分開處理,切勿使用echo方法,在Send後自行Receive

    SocketArgsEvent分開兩個instance便行了!

    上面版主的方法是對的,我開始也是這樣,但就是學了其他例子在Send後再BeginReceive,結果弄了半天才明白,網上的例子真的把新手害得很慘!

    多謝各位幫忙~

    2011年6月29日 上午 09:43
  • 網路上的例子常常是以  "發送 -->回應" 模型為主, 所以他們會這麼寫, 也不能算錯. 只是恰好和你的應用方向不一樣而已.

    通常撞牆的好處是, 如果你突破了這道牆, 你對這東西的體會就會更深一層, 也未嘗不是好事.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2011年7月1日 上午 02:44
    版主