分享一个C#编写简单的聊天程序(详细介绍)

2019-12-30 11:38:16王旭

这段代码可以用朴实无华来形容,所以我们直接看下一段。

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委托。