C#异步接收数据(Socket)
2009年12月17日
最近DO一个远程服务器监控程序,使用了.NET的socket组件来开发。一般,我都习惯先写一个简单的控制台小程序测试一下与服务器通信是否正常。
AMITest.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Text;
namespace AMITest
{
class AppConsole
{
[STAThread]
static void Main(string[] args)
{
// 连接到服务器
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.20"), 5038);
clientSocket.Connect(serverEndPoint);
// 发送登陆指令
clientSocket.Send(Encoding.ASCII.GetBytes("Action: Login\r\nUsername: mark\r\nSecret: mysecret\r\nActionID: 1\r\n\r\n"));
int bytesRead = 0;
do
{
byte[] buffer = new byte[1024];
bytesRead = clientSocket.Receive(buffer);
string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(response); //输出接收回来的数据
if(Regex.Match(response, "Message: Authentication accepted", RegexOptions.IgnoreCase).Success) //解释服务器反馈的结果是否登陆成功
{
// 成功之后发送一个Ping指令
clientSocket.Send(Encoding.ASCII.GetBytes("Action: Ping\r\nActionID: 2\r\n\r\n"));
}
}while(bytesRead != 0);
Console.WriteLine("断开服务器.");
Console.ReadLine();
}
}
}
如果一切正常,再做一个UI,用多线程来测试所有指令。UI里有一个txtMsg的textbox控件专门显示与服务器的通信结果。由于socket组件打开的线程已经“注入”到了主控制线程中,它取得了主线程的控制。只要这个线程不返回,那么主线程将永远都无法响应。就算新开的线程中不使用无限循环,即使可以返回了。这种方式的使用多线程也失去了它本来的意义。那么,最好的解决方法是使用.net的异步调用来解决这个问题,所以,不能接收数据后,直接就把数据输出到控件里。处理的方法如下:
AMITestUI.cs
using System;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace AsyncAMITest
{
public partial class FMain : Form
{
private delegate void doit(string str); //代理
public FMain()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(sendaction);
thread.IsBackground = true;
thread.Start();
}
private void log(string str)
{
if (this.txtmsg.InvokeRequired)//等待异步
{
doit fc = new doit(log);
this.Invoke(fc,new object[]{str});//通过代理调用刷新方法
}
else
{
this.txtmsg.AppendText(str + "\r\n");
}
}
private void sendaction()
{
Socket sk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.20"), 5038);
sk.Connect(serverEndPoint);
sk.Send(Encoding.ASCII.GetBytes("Action: Login\r\nUsername: mark\r\nSecret: mysecret\r\nActionID: 1\r\n\r\n"));
int bytesRead = 0;
string response = "";
do
{
byte[] buffer = new byte[1024];
bytesRead = sk.Receive(buffer);
response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(response);
Thread.Sleep(100);
//this.txtmsg.AppendText(str + "\r\n"); //不能够这样,这样会导致消息阻塞,界面无响应
log(response);
} while (bytesRead != 0);
}
}
}
表红色的都是关键代码,通过等待异步,我们就不会总是持有主线程的控制,这样就可以在不发生跨线程调用异常的情况下完成多线程对winform多线程控件的控制了。
在DELPHI里,也遇到同样的问题,不过DELPHI的解决方法也很简单,就是建立一个线程类(TThread),在线程里编写一个VCL数据显示的函数,然后用Synchronize来同步该函数,就可以解决了。DELPHI自带了TcpServer和TcpClient两个VCL组件做的聊天例子程序,在此就略过DELPHI的相关代码了。
2009年12月17日
最近DO一个远程服务器监控程序,使用了.NET的socket组件来开发。一般,我都习惯先写一个简单的控制台小程序测试一下与服务器通信是否正常。
AMITest.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Text;
namespace AMITest
{
class AppConsole
{
[STAThread]
static void Main(string[] args)
{
// 连接到服务器
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.20"), 5038);
clientSocket.Connect(serverEndPoint);
// 发送登陆指令
clientSocket.Send(Encoding.ASCII.GetBytes("Action: Login\r\nUsername: mark\r\nSecret: mysecret\r\nActionID: 1\r\n\r\n"));
int bytesRead = 0;
do
{
byte[] buffer = new byte[1024];
bytesRead = clientSocket.Receive(buffer);
string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(response); //输出接收回来的数据
if(Regex.Match(response, "Message: Authentication accepted", RegexOptions.IgnoreCase).Success) //解释服务器反馈的结果是否登陆成功
{
// 成功之后发送一个Ping指令
clientSocket.Send(Encoding.ASCII.GetBytes("Action: Ping\r\nActionID: 2\r\n\r\n"));
}
}while(bytesRead != 0);
Console.WriteLine("断开服务器.");
Console.ReadLine();
}
}
}
如果一切正常,再做一个UI,用多线程来测试所有指令。UI里有一个txtMsg的textbox控件专门显示与服务器的通信结果。由于socket组件打开的线程已经“注入”到了主控制线程中,它取得了主线程的控制。只要这个线程不返回,那么主线程将永远都无法响应。就算新开的线程中不使用无限循环,即使可以返回了。这种方式的使用多线程也失去了它本来的意义。那么,最好的解决方法是使用.net的异步调用来解决这个问题,所以,不能接收数据后,直接就把数据输出到控件里。处理的方法如下:
AMITestUI.cs
using System;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace AsyncAMITest
{
public partial class FMain : Form
{
private delegate void doit(string str); //代理
public FMain()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(sendaction);
thread.IsBackground = true;
thread.Start();
}
private void log(string str)
{
if (this.txtmsg.InvokeRequired)//等待异步
{
doit fc = new doit(log);
this.Invoke(fc,new object[]{str});//通过代理调用刷新方法
}
else
{
this.txtmsg.AppendText(str + "\r\n");
}
}
private void sendaction()
{
Socket sk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.20"), 5038);
sk.Connect(serverEndPoint);
sk.Send(Encoding.ASCII.GetBytes("Action: Login\r\nUsername: mark\r\nSecret: mysecret\r\nActionID: 1\r\n\r\n"));
int bytesRead = 0;
string response = "";
do
{
byte[] buffer = new byte[1024];
bytesRead = sk.Receive(buffer);
response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(response);
Thread.Sleep(100);
//this.txtmsg.AppendText(str + "\r\n"); //不能够这样,这样会导致消息阻塞,界面无响应
log(response);
} while (bytesRead != 0);
}
}
}
表红色的都是关键代码,通过等待异步,我们就不会总是持有主线程的控制,这样就可以在不发生跨线程调用异常的情况下完成多线程对winform多线程控件的控制了。
在DELPHI里,也遇到同样的问题,不过DELPHI的解决方法也很简单,就是建立一个线程类(TThread),在线程里编写一个VCL数据显示的函数,然后用Synchronize来同步该函数,就可以解决了。DELPHI自带了TcpServer和TcpClient两个VCL组件做的聊天例子程序,在此就略过DELPHI的相关代码了。