修改
40多个观点,只有一个能够提供帮助,这个问题不值得投票吗? ;)
/编辑
我再次提到c#中的套接字编程问题。
我已经设置了一个带有自制控制台(richTextBox)的小服务器,它正在监听多个连接。
一切正常,但如果我关闭其中一个连接的客户端,程序将给我一个异步错误,并在控制台上不断从客户端写入一条空白消息(事件:ClientReceivedHandler)
所以有人可以帮我找到问题吗?
我的代码:
SERVER - 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 );
}
}
}
SERVER - 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;
}
}
SERVER - 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
感谢所有能帮助我的人。
P.S。如果有人需要查看问题,我可以上传VS项目文件或服务器和客户端的.exe文件。
更新:
第一个问题(异步错误)出现在try-catch的Listener.cs文件中。 catch部分中的messagebox给出了这个文本:
“IAsyncResult对象未从相应的异步方法返回。参数:asyncResult”
这是在调用“ServerStop()”方法时出现的。
第二个问题是SERVER-Form1.cs中client_Received()方法中的任何位置。
它似乎不断接收空白数据,因此它在console / richTextBox中输出一条空白消息
我不熟悉c#中的socket-logic,所以我无法弄清楚locig-error发生在代码中的哪个位置。
希望有人找到它。
编辑:
zip文件中的项目文件 http://ace-acid.no-ip.org/GameServer/
(c)中
答案 0 :(得分:1)
第一个问题(异步错误)出现在try-catch的Listener.cs文件中。 catch部分中的messagebox给出了这个文本:
“未从相应的对象返回IAsyncResult对象 异步方法。参数:asyncResult“
在 Listener.cs - GameServer 中,可以安全地忽略此错误。我相信忽略错误不会在启动/停止套接字期间引起问题。问题是您忘记在Listening = false;
的末尾添加Stop()
,以便应用程序可以再次调用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中输出一条空白消息
是的,由于存在连接,它将始终收到空白数据。您可以在以下屏幕截图中看到这一点
要解决此问题,您首先需要检查Encoding
中使用byte[] data
的返回字符串是否为空。然后,您可以在console / 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
选项卡下,即使服务器断开连接,列表也永远不会被清除。只需拨打dataGridViewConnections.Rows.Clear();
即可在调用ServerStop()
时清除连接列表。
示例强>
在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);
}
}
这就是我现在所能发现的全部内容,如果我发现任何相关内容,我会及时通知你。
或者,您可以找到属于命名空间的项目文件GameServer
here
谢谢, 我希望你觉得这很有帮助:)