程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C#編寫簡單的聊天程序(5)

C#編寫簡單的聊天程序(5)

編輯:關於C語言

編寫程序代碼

如果你已經看完了上面一節C#網絡編程,那麼本章完全沒有講解的必要了,所以我只列出代碼,對個別值得注意的地方稍微地講述一下。首 先需要了解的就是,我們采用的是三個模式中開發起來難度較大的一種,無服務器參與的模式。還有就是我們沒有使用廣播消息,所以需要提 前知道連接到的遠程主機的地址和端口號。

1.實現IMessageSender接口

public class MessageSender : IMessageSender {

    TcpClient clIEnt;
    Stream streamToServer;

    // 連接至遠程
    public bool Connect(IPAddress ip, int port) {
        try {
            client = new TcpClIEnt();
            clIEnt.Connect(ip, port);
            streamToServer = clIEnt.GetStream(); // 獲取連接至遠程的流
            return true;
        } catch {
            return false;
        }
    }

    // 發送消息
    public bool SendMessage(Message msg) {
        try {
            lock (streamToServer) {
                byte[] buffer = Encoding.Unicode.GetBytes(msg.ToString());
                streamToServer.Write(buffer, 0, buffer.Length);
                return true;
            }
        } catch {
            return false;
        }
    }

    // 注銷
    public void SignOut() {
        if (streamToServer != null)
            streamToServer.Dispose();
        if (clIEnt != null)
            clIEnt.Close();
    }
}

這段代碼可以用樸實無華來形容,所以我們直接看下一段。

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved