上一節我們搭建了即時通信程序的登錄端,這一節我們要實現即時通信程序的主客戶端的搭建,也就是聊天、發文件端的創建。講完這一節之后,我們就可以自己實現一個即時通信程序了。好了,先上一個圖。
該UI布局如下:有一個ListBox用來顯示當前在線用戶命名為onLineList
三個文本框分別為:txtchatContent、txtsendMsg、txtsendFile,分別表示:聊天的記錄、發送信息框、要發送的文件框
四個按鈕分別為:btnsendMsg、btnsendAll、button1、button2,分別表示:
發送消息、群發消息、選擇要發送的文件、發送文件
下面我們通過具體的示例來向大家一步步的進行講解。
首先需要創建以下全局變量:
private ClientLogin clientLogin;//用來存儲從ClientLogin傳過來的client參數 private string ClientName;//表示當前的用戶名 private TcpClient tcpClient;//全局的TcpClient對象,用來負責客戶端的連接、通信 private NetworkStream netStream;//全局的NetworkStream對象,負責發送、接受信息
接着,我們需要在構造函數里做如下處理:
public ChatClient(ClientLogin clogin) { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false;//取消跨線程檢測,允許非UI線程操作UI元素 skinEngine1.SkinFile = "SteelBlack.ssk";//加載皮膚文件 clientLogin = clogin;//將從登陸框傳入的參數賦值給clientLogin lbname.Text += clientLogin.LoginName; tcpClient = clogin.TCPClient;將從登陸框傳入的clogin的TCPClient屬性賦值給全局tcpClient,負責客戶端的通信 if (tcpClient != null) { netStream = tcpClient.GetStream(); txtchatContent.Text = "連接成功!\r\n"; Thread listenThread = new Thread(new ThreadStart(Listening));//專門啟動一個線程負責與服務端溝通 listenThread.Start(); }
我們在Listening函數中來處理具體監聽操作,我們在第一節中已經定制了客戶端的通信,在Listening函數里有用到,在這里我們再溫習一下。
1、先判斷接收到的第一個字節是否為0,如果為0則直接保存為文件,如果不為0,則進行如下的判斷。
2、接收到的第一個字節不為0,即收到的信息為字符串。
2.1、如果接收到的字符串格式為“OnLine|登錄用戶名|”,則顯示登錄用戶名
2.2、如果接受到的字符串格式為“Off|用戶名1、用戶名2、用戶名3.....|”,如果有用戶登錄或者離開則更新在線用戶列表
2.3、如果收到的為其他格式的字符串,則直接添加在聊天窗口里,顯示聊天信息。
public void Listening() { int length=0; while (true) { //先創建一個10M的緩存區 byte[] Msg = new byte[1024 * 1024 * 10]; //將傳輸的流讀取到該緩沖區中 length=netStream.Read(Msg, 0, Msg.Length); //如果緩沖區的第一字節為0則為文件,否則,則為消息 if (Msg[0] == 0) { //直接新建一個文件流保存傳輸過來的文件 SaveFileDialog sfDialog = new SaveFileDialog(); if (sfDialog.ShowDialog() == DialogResult.OK) { FileStream fs = new FileStream(sfDialog.FileName, FileMode.OpenOrCreate, FileAccess.Write); fs.Write(Msg, 1, length - 1); MessageBox.Show("文件傳輸完畢!"); } } else { //第一個字節不為0,肯定為消息字符串 string msg = Encoding.Default.GetString(Msg); string[] results = msg.Split(new char[] { '|' }); if (results[0]=="OnLine")//如果有人上線,則顯示上線人姓名 { txtchatContent.Text += results[1]+"\r\n"; } else if (results[0] == "Off")//有人上線或離開,更新在線人員列表 { string[] clients = results[1].Split(new char[] { '、' }); onlineList.Items.Clear(); for (int i = 0; i < clients.Length-1; i++) { onlineList.Items.Add(clients[i]); } } else { txtchatContent.Text += msg+"\r\n"; } } } }
接受我們需要在btnsendMsg按鈕的Click事件的處理函數做如下處理:
需要將發送的消息做特殊的處理,發送信息的格式為:MSG|接受者姓名|發送者姓名|發送內容|
在發送信息前,要選擇要發送的用戶,如果沒有選擇用戶則不能進行發送信息。
private void btnsendMsg_Click(object sender, EventArgs e) { if (onlineList.SelectedItem!=null) { if (!string.IsNullOrEmpty(txtsendMsg.Text)) { try { string msg ="MSG|"+ onlineList.SelectedItem.ToString() + "|"+clientLogin.LoginName+"|" + txtsendMsg.Text+"\r\n|"; byte[] buffer = Encoding.Default.GetBytes(msg); this.netStream.Write(buffer, 0, buffer.Length); } catch (Exception e2) { MessageBox.Show("信息發送異常:" + e2.Message); } } else { MessageBox.Show("要發送的信息不能為空!"); } } else { MessageBox.Show("請選擇要發送的對象!"); } }
如果我們需群發信息,則直接點擊群發信息按鈕就可以了,所以我們在btnSendAll按鈕中做如下處理,將需要群發的信息進行加工,信息格式為:MSG|發送者姓名|發送內容|
private void btnsendAll_Click(object sender, EventArgs e) { string msg = "MSGALL|"+clientLogin.LoginName+"|" + txtsendMsg.Text+"\r\n|"; byte[] buffer = Encoding.Default.GetBytes(msg); this.netStream.Write(buffer, 0, buffer.Length); }
如果我們需要發送文件,則需要先選擇要發送的文件,所以在button1的Click事件的處理函數中做如下處理,選擇要發送的文件(文件大小不能超過10M)
private void button1_Click(object sender, EventArgs e) { OpenFileDialog ofDialog = new OpenFileDialog(); if (ofDialog.ShowDialog() == DialogResult.OK) { FileInfo f = new FileInfo(ofDialog.FileName); if (f.Length < 1024 * 1224 * 10) { txtsendFile.Text = ofDialog.FileName; } else { MessageBox.Show("不好意思!您傳輸的文件大於10M,請分割后再傳!"); }
選擇文件后就可以點擊發送,在button2的Click事件的處理函數中做如下處理,將要發送的文件讀取到字節數組中,但是需要將該字節數組的首字節設置為0,然后才進行發送。
具體代碼如下:
private void button2_Click(object sender, EventArgs e) { byte[] buffer = new byte[1024 * 1024 * 10]; try { FileStream fs = new FileStream(txtsendFile.Text, FileMode.Open, FileAccess.Read); int length = fs.Read(buffer, 0, buffer.Length); byte[] buffers=new byte[length+1]; buffers[0] = 0; //將bytes里的數據從第0個開始拷貝到filetype里,從第一個位置開始,一共拷貝length個數據 Buffer.BlockCopy(buffer, 0, buffers, 1, length); this.netStream.Write(buffers, 0, buffers.Length); FileStream fsq = new FileStream(@"D:\1.txt", FileMode.Create, FileAccess.Write); fsq.Write(buffers, 0,length+1); fsq.Close(); } catch (Exception e2) { MessageBox.Show("文件傳輸異常:" + e2.Message); } } } }
好了到這里我們已經完成了整個即時通信程序的設計和實現,希望可以讓大家對.NET平台下網絡通信有新的認識,也希望能夠對大家有所幫助。這個通信程序里我們主要用到的知識要點有TCP/UDP 通信(TCPListener/TCPClient)、Socket套接字、多線程、文件的操作等。如果有對以上知識點不清楚的朋友可以參看我以前寫的相關的文章,希望可以對大家有所幫助。好了這一系列的文章就到這里了。