这段代码可以用朴实无华来形容,所以我们直接看下一段。
2.实现IMessageReceiver接口
public delegate void PortNumberReadyEventHandler(int portNumber);
public class MessageReceiver : IMessageReceiver {
public event MessageReceivedEventHandler MessageReceived;
public event ConnectionLostEventHandler ClientLost;
public event ClientConnectedEventHandler ClientConnected;
// 当端口号Ok的时候调用 -- 需要告诉用户界面使用了哪个端口号在侦听
// 这里是业务上体现不出来,在实现中才能体现出来的
public event PortNumberReadyEventHandler PortNumberReady;
private Thread workerThread;
private TcpListener listener;
public MessageReceiver() {
((IMessageReceiver)this).StartListen();
}
// 开始侦听:显示实现接口
void IMessageReceiver.StartListen() {
ThreadStart start = new ThreadStart(ListenThreadMethod);
workerThread = new Thread(start);
workerThread.IsBackground = true;
workerThread.Start();
}
// 线程入口方法
private void ListenThreadMethod() {
IPAddress localIp = IPAddress.Parse("127.0.0.1");
listener = new TcpListener(localIp, 0);
listener.Start();
// 获取端口号
IPEndPoint endPoint = listener.LocalEndpoint as IPEndPoint;
int portNumber = endPoint.Port;
if (PortNumberReady != null) {
PortNumberReady(portNumber); // 端口号已经OK,通知用户界面
}
while (true) {
TcpClient remoteClient;
try {
remoteClient = listener.AcceptTcpClient();
} catch {
break;
}
if (ClientConnected != null) {
// 连接至本机的远程端口
endPoint = remoteClient.Client.RemoteEndPoint as IPEndPoint;
ClientConnected(endPoint); // 通知用户界面远程客户连接
}
Stream streamToClient = remoteClient.GetStream();
byte[] buffer = new byte[8192];
while (true) {
try {
int bytesRead = streamToClient.Read(buffer, 0, 8192);
if (bytesRead == 0) {
throw new Exception("客户端已断开连接");
}
string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
if (MessageReceived != null) {
MessageReceived(msg); // 已经收到消息
}
} catch (Exception ex) {
if (ClientLost != null) {
ClientLost(ex.Message); // 客户连接丢失
break; // 退出循环
}
}
}
}
}
// 停止侦听端口
public void StopListen() {
try {
listener.Stop();
listener = null;
workerThread.Abort();
} catch { }
}
}
这里需要注意的有这样几点:我们StartListen()为显式实现接口,因为只能通过接口才能调用此方法,接口的实现类看不到此方法;这通常是对于一个接口采用两种实现方式时使用的,但这里我只是不希望MessageReceiver类型的客户调用它,因为在MessageReceiver的构造函数中它已经调用了StartListen。意思是说,我们希望这个类型一旦创建,就立即开始工作。我们使用了两个嵌套的while循环,这个它可以为多个客户端的多次请求服务,但是因为是同步操作,只要有一个客户端连接着,我们的后台线程就会陷入第二个循环中无法自拔。所以结果是:如果有一个客户端已经连接上了,其它客户端即使连接了也无法对它应答。最后需要注意的就是四个事件的使用,为了向用户提供侦听的端口号以进行连接,我又定义了一个PortNumberReadyEventHandler委托。










