【问题标题】:Multi Client Server - receive event goes crazy after client disconnection & async error多客户端服务器 - 客户端断开连接和异步错误后接收事件变得疯狂
【发布时间】:2012-10-18 00:54:45
【问题描述】:

编辑

40+ 意见,只有一个能够提供帮助,这个问题不值得点赞吗? ;)

/编辑

我在 c# 中的套接字编程有一些问题。

我用自制控制台 (richTextBox) 设置了一个小服务器,它正在侦听多个连接。

一切正常,除了如果我关闭一个连接的客户端,程序会给我一个异步错误,并且在控制台上它不断地从客户端写入一条空白消息(事件:ClientReceivedHandler)

那么谁能帮我找出问题所在??

我的代码:

服务器 - Form1.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;


namespace GameServer {
public partial class Form1 : Form {

    // ### MAIN
    public bool serverRunning = false;
    public int serverListenPort = 6666;

    static Listener l;

    public Form1() {
        InitializeComponent();

        l = new Listener( serverListenPort );
        l.SocketAccepted += new Listener.SocketAcceptedHandler( l_SocketAccepted );
    }

    void l_SocketAccepted( Socket e ) {
        Client client = new Client( e );
        client.Received += new Client.ClientReceivedHandler( client_Received );
        client.Disconnected += new Client.ClientDisconnectedHandler( client_Disconnected );

        Invoke( ( MethodInvoker )delegate {
            dataGridViewConnections.Rows.Add( client.ID, client.EndPoint.ToString() );
            consoleWrite( DateTime.Now + " - " + client.EndPoint + " connected to server.\n\n", Color.Lime );
        } );
    }

    void client_Received( Client sender, byte[] data ) {
        Invoke( ( MethodInvoker )delegate {
            consoleWrite( DateTime.Now + "-" + sender.EndPoint + " :\n" + Encoding.Default.GetString( data ) + "\n\n", Color.White );;
        } );
    }

    void client_Disconnected( Client sender ) {
        Invoke( ( MethodInvoker )delegate {
            for( int i = 0; i < dataGridViewConnections.Rows.Count; i++ ) {
                if( dataGridViewConnections.Rows[i].Cells[0].Value.ToString() == sender.ID.ToString() ) {
                    dataGridViewConnections.Rows.RemoveAt( i );
                    consoleWrite( DateTime.Now + " - " + sender.EndPoint + " disconnected from server.\n\n", Color.OrangeRed );
                    break;
                }
            }
        } );
    }

    private void checkBox1_Click(object sender, EventArgs e) {
        checkBox1.Enabled = false;
        if( !serverRunning ) {
            consoleWrite( DateTime.Now + " " + markerSystem + "start\n", Color.White );
            ServerStart();
            checkBox1.Text = "Stop";
        } else {
            consoleWrite( DateTime.Now + " " + markerSystem + "stop\n", Color.White );
            ServerStop();
            checkBox1.Text = "Start";
        }
        checkBox1.Enabled = true;
    }

    private void ServerStart() {
        if( !serverRunning ) {
            consoleWrite( "* Starting server . . .\n", Color.Orange );
            // Start Server
            l.Start();
            serverRunning = true;
            consoleWrite( "* Server started !\n", Color.Lime );
            consoleWrite("Listening on port " + serverListenPort + ".\n\n", Color.White );
        } else {
            consoleWrite( "* ERROR: Server already started !\n\n", Color.Red );
        }
    }

    private void ServerStop() {
        if( serverRunning ) {
            consoleWrite( "* Stopping server . . .\n", Color.Orange );
            // Stop Server
            l.Stop();
            serverRunning = false;
            consoleWrite( "* Server stopped !\n\n", Color.Lime );
        } else {
            consoleWrite( "* ERROR: Server already stopped !\n\n", Color.Red );
        }
    }

    private string markerSystem = "@System -> ";
    private string marker = "-> ";

    private void consoleWrite( string text, Color color ) {
        consoleText.SelectionStart = consoleText.Text.Length;
        consoleText.SelectionLength = 0;
        consoleText.SelectionColor = color;
        consoleText.AppendText( text );
    }
}
}

服务器 - Listener.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

namespace GameServer {
    class Listener {
        Socket s;

        public bool Listening {
            get;
            private set;
        }

        public int Port {
            get;
            private set;
        }

        public  Listener( int port ) {
            Port = port;
            s = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
        }

        public void Start() {
            if( Listening ) {
                return;
            }

            s.Bind( new IPEndPoint( 0, Port ) );
            s.Listen(0);

            s.BeginAccept( callback, null );
            Listening = true;
        }

        public void Stop() {
            if( !Listening ) {
                return;
            }

            s.Close();
            s.Dispose();
            s = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
        }

        void callback( IAsyncResult ar ) {
            try {
                Socket s = this.s.EndAccept( ar );

                if( SocketAccepted != null ) {
                    SocketAccepted( s );
                }

                this.s.BeginAccept( callback, null );
            } catch( Exception ex ) {
                MessageBox.Show( ex.Message );
            }
        }

        public delegate void SocketAcceptedHandler( Socket e );
        public event SocketAcceptedHandler SocketAccepted;
    }
}

服务器 - Client.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

namespace GameServer {
    class Client {
        public string ID {
            get;
            private set;
        }

        public IPEndPoint EndPoint {
            get;
            private set;
        }

        Socket sck;

        public Client( Socket accepted ) {
            sck = accepted;
            ID = Guid.NewGuid().ToString();
            EndPoint = ( IPEndPoint )sck.RemoteEndPoint;
            sck.BeginReceive( new byte[] { 0 }, 0, 0, 0, callback, null );
        }

        void callback( IAsyncResult ar ) {
            try {
                sck.EndReceive( ar );

                byte[] buf = new byte[8192];
                int rec = sck.Receive( buf, buf.Length, 0 );

                if( rec < buf.Length ) {
                    Array.Resize<byte>( ref buf, rec );
                }

                if( Received != null ) {
                    Received( this, buf );
                }

                sck.BeginReceive( new byte[] { 0 }, 0, 0, 0, callback, null );
            } catch( Exception ex ) {
                MessageBox.Show( ex.Message );
                Close();

                if( Disconnected != null ) {
                    Disconnected( this );
                }
            }
        }

        public void Close() {
            sck.Close();
            sck.Dispose();
        }

        public delegate void ClientReceivedHandler( Client sender, byte[] data );
        public delegate void ClientDisconnectedHandler( Client sender );

        public event ClientReceivedHandler Received;
        public event ClientDisconnectedHandler Disconnected;
    }
}

客户 - Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

namespace GameClient {
    public partial class Form1:Form {
        Socket sck;

        public Form1() {
            InitializeComponent();

            sck = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
            sck.Connect( "127.0.0.1", 6666 );
        }

        private void button1_Click( object sender, EventArgs e ) {
            int s = sck.Send( Encoding.Default.GetBytes( textBox1.Text ) );

            if( s > 0 ) {
                textBox1.Text = "";
            }
        }

        private void button2_Click( object sender, EventArgs e ) {
            sck.Close();
            sck.Dispose();
        }
    }
}

我真的很高兴让这个服务器稳定,这样我就可以开始实现多人游戏服务器逻辑了:D

感谢所有可以帮助我的人。

附:如果有人需要查看实际问题,我可以上传 VS 项目文件或服务器和客户端的 .exe 文件。

更新:

第一个问题(异步错误)出现在 try-catch 中的 Listener.cs 文件中。 catch 部分中的消息框给出了以下文本:

“IAsyncResult对象没有从相应的异步方法返回。参数:asyncResult”

这在调用“ServerStop()”方法时出现。

第二个问题是在 SERVER-Form1.cs 中的 client_Received() 方法中的任何地方。

好像是不断收到空白数据,所以在console/richTextBox输出空白信息

我不熟悉 c# 中的套接字逻辑,所以我无法弄清楚代码中发生 locig-error 的位置。

希望有人找到它。

编辑:

zip 文件中的项目文件 http://ace-acid.no-ip.org/GameServer/

(c)

【问题讨论】:

  • @Ace 哦,是的,对不起,我现在知道了。感谢您提供信息。当我单击停止时,我也收到了错误。我会尽力解决这个问题:)
  • @Ace:同样,您将使用重现问题所需的最少代码量获得更好的答案。许多用户(包括我)根本不会阅读这么多代码。
  • 标题中的“c#”可以帮助其他在 google 上搜索该问题的人。如果您愿意,您可以更改我所有的问题标题,它们都包含这些信息。就像你在一些问题中所做的那样。所有代码都是理解问题所必需的。我不能简单地发布代码片段,因为问题不能定位在它发生的部分,因为它是一个逻辑问题。不想阅读代码的人应该只发布问题而不是回答问题。 - 我的意见
  • @Ace 感谢 Ace,我几乎解决了第一个问题。现在服务器可以正常断开连接了。我正在追踪第二个错误! :)
  • @Ace 我已经成功解决了这两个问题。第一个花了我一些时间,虽然它很容易被发现。我会在一分钟内发布我的答案:)

标签: c# sockets asynchronous connection client


【解决方案1】:

第一个问题(异步错误)出现在 try-catch 中的 Listener.cs 文件中。 catch 部分中的消息框给出了以下文本:

"IAsyncResult 对象没有从相应的返回 异步方法。参数:asyncResult"

Listener.cs - GameServer 中,可以安全地忽略此错误。我相信忽略错误不会在启动/停止套接字期间引起问题。问题是您忘记在Stop() 的末尾添加Listening = false;,以便应用程序可以再次调用Start() 来启动套接字。否则,如果停止,套接字将永远不会启动。

示例

public void Stop()
{
     if (!Listening)
     {
          return;
     }

     s.Close();
     s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
     Listening = false; //Set Listening to False to start the socket again
}

虽然设置Listening = false; 会解决重启套接字的问题,但它不会阻止您获得上面提到的异常,因为该异常不依赖于Listening

我已经尝试过解决这个问题,但它总是告诉我无法访问已处置的对象s(socket),因此我认为异常是基于这一行s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

无论如何都可以安全地忽略该异常,我已经忽略了它并且它工作得很好。要忽略它,只需创建一个try/catch 块来获取异常,如果异常消息包含asyncResult,则忽略弹出一个带有异常的框。

示例

void callback(IAsyncResult ar)
{

      try
      {

           Socket s = this.s.EndAccept(ar);
           if (SocketAccepted != null)
           {
                SocketAccepted(s);
           }
           this.s.BeginAccept(callback, null);
      }
      catch (Exception ex)
      {
           if (!ex.Message.Contains("asyncResult")) //Proceed only if the exception does not contain asyncResult
           {
                MessageBox.Show(ex.Message);
           }
      }
}

好像是不断收到空白数据,所以在console/richTextBox输出空白信息

是的,因为有连接,它总是会收到空白数据。您可以在以下屏幕截图中看到这一点

要解决此问题,您首先需要检查使用来自byte[] dataEncoding 返回的字符串是否为空。然后,你可以在控制台/richtextbox中写一个新行

示例

Form1.cs - GameServer

client_Received(Client sender, byte[] data)替换为以下代码

void client_Received(Client sender, byte[] data)
{
     if (Encoding.Default.GetString(data) != "") //Proceed only if data is not blank
     {
          Invoke((MethodInvoker)delegate
          {
               consoleWrite(DateTime.Now + "-" + sender.EndPoint + " :\n" + Encoding.Default.GetString(data) + "\n\n", Color.White); ;
            });
          }
     }
}

应用修复后,这是输出

还有一个您可能忘记提及的问题,在Connections 选项卡下,即使服务器断开连接,列表也永远不会清除。在调用ServerStop() 时,只需调用dataGridViewConnections.Rows.Clear(); 即可清除连接列表。

示例

在 Form1.cs - GameServer

ServerStop()替换为以下代码

private void ServerStop()
{
     if (serverRunning)
     {
          consoleWrite("* Stopping server . . .\n", Color.Orange);
          l.Stop();
          serverRunning = false;
          consoleWrite("* Server stopped !\n\n", Color.Lime);
          dataGridViewConnections.Rows.Clear(); // Clear connections

     }
     else
     {
          consoleWrite("* ERROR: Server already stopped !\n\n", Color.Red);
     }
 }

目前我只能检测到这些,如果发现任何相关信息,我会及时通知您。

或者,您可以找到属于命名空间GameServerhere的项目文件

谢谢,
希望对您有所帮助:)

【讨论】:

  • 我也上传了项目文件。你可以让他们here。祝你有美好的一天:)
  • wenn 首先:清除 datagridview 是下一个任务,我专注于套接字逻辑 XD 我稍后会清除功能。第二:感谢您有时间看我制作的那些东西。我想知道断开连接后是否真的得到空白数据...,如果是这样,如果我有 4 个示例超过 500 个连接,并且它们都在发送空白数据(没有连接),是否存在带宽过载的情况??? ) 到服务器,它会处理它.. ?我不能为我正在创建的所有套接字创建一个池,并检测其中一个是否断开连接并删除它吗?
  • 和 4 异步错误:在制作该教程的家伙 youtube 频道上,还有两个视频称为 async any blabla,这也是遵循该教程的解决方案吗?我认为异步处理是一种特殊的做事方式(同样,我对网络编程一无所知)
  • 现在我会将此标记为答案,直到任何爱因斯坦来告诉我我不能忽略该错误消息^^
  • @Ace 非常感谢。我正在研究这个问题。我还注意到游戏客户端在富文本框中没有收到任何消息。这是您的应用程序中的默认行为吗?
猜你喜欢
  • 2014-06-29
  • 2016-07-26
  • 2018-02-09
  • 1970-01-01
  • 2020-07-21
  • 2012-05-02
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
相关资源
最近更新 更多