UDP 广播通信应用的一些摘要
做了一个简单的多机屏幕查看程序,摘录其中一些简单代码。
获取本机 IP 地址
var localIpAddressList = Dns.GetHostEntry(hostName).AddressList; //取得本机IP
_localIp = localIpAddressList[0];
UDP 广播地址的计算
255.255.255.255是全局广播地址,虽然这个地址也能用来广播,但是在多网卡情况下不能很好的绑定到特定网卡上,对特定网卡,计算它的广播地址应该是网卡对应的 掩码取反,然后和 IP 地址求 OR.
没有标题的FORM, 只需要将 Form 的 min, max, control box 去掉,标题设为空即可,要在这种FORM上,按下鼠标能够拖动窗口,有好几种方法,个人觉得最好的是以下这种:
//const int WM_NCHITTEST = 0x84;
const int HT_CAPTION = 0x2;
//const int HT_CLIENT = 0x1;
if (e.Button == MouseButtons.Left)
{
Capture = false;
Message msg = Message.Create(Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
WndProc(ref msg);
}
关于UPD通信的可靠性,和数据包的大小密切相关,在设置了UDP DontFragment = flase 参数后,由于允许对UDP包进行IP分片,因此理论上可以发送任意大小的UDP包,但是一般的 MTU 大小都只有 14xx 字节,因此超过 MTU 大小的 UDP包将被分片传输,这将导致丢包率上升,用PNG格式传输屏幕截图,大小一般在10K左右,有时候会连续10来个包丢失的。修改为只发送心跳广播信号(1字节),图片另外通过TCP通道传输,速度其实也不慢,一个远程调用,执行时间在0.1秒左右。
DotNet 远程调用在很多情况下可同样起到网络传输的效果,没有特别要求时,完全可以使用远程调用来代替网络编程。
远程调用的服务端注册代码:
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.ApplicationName = ServiceName;
WellKnownServiceTypeEntry WKSTE = new WellKnownServiceTypeEntry(typeof(RemotePanel), ServiceName, WellKnownObjectMode.SingleCall);
RemotingConfiguration.RegisterWellKnownServiceType(WKSTE);
RemotingConfiguration.CustomErrorsEnabled(false);
RemotingConfiguration.CustomErrorsMode = CustomErrorsModes.Off;
采用 WellKnownServiceTypeEntry 方式注册,则客户端可不必进行注册,直接使用 getObject 即可获取远程对象。
RemotePanel obj = null;
try
{
obj = (RemotePanel) Activator.GetObject(typeof(RemotePanel), url);
}
catch (Exception e)
{
Program.Log(null, e.ToString());
}
return obj;
创建远程对象或调用远程方法的时候,如果出现网络故障,或者远程程序已退出,则会发生异常,特别如果远程程序未启动,则调用线程将被Block 较长的时间后才发出连接失败的异常,这个调用如果出现在有界面的程序中会照成程序假死的现象,简单的解决办法是让远程程序通过 UDP 广播心跳,客户端收到心跳后立即进行远程调用。
在多线程程序中,如果在一个线程中访问另一个线程创建的 Form,则将引发异常,最简单的解决方法是在访问代码中检测是否从另一线程访问,通过委托句柄进行访问,例如以下在窗口显示日志信息的代码,日志可能从其他线程发生过来:
/// <summary>
/// Logs the specified MSG.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="msg">The MSG.</param>
private void log(object sender, String msg)
{
TextBox textBox;
if (sender is Client)
{
textBox = txtLog2;
}
else
{
textBox = txtLog;
}
if (textBox.InvokeRequired)
{
LogCallBack d = log;
Invoke(d, new[] {sender, msg });
return;
}
if (textBox.TextLength > 60000)
{
textBox.Text = textBox.Text.Substring(30000);
}
textBox.AppendText(DateTime.Now.ToShortTimeString());
textBox.AppendText("-");
textBox.AppendText(msg);
textBox.AppendText("\r\n");
}