基于Socket的聊天室_第1頁
基于Socket的聊天室_第2頁
基于Socket的聊天室_第3頁
基于Socket的聊天室_第4頁
基于Socket的聊天室_第5頁
已閱讀5頁,還剩20頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

一、服務器/客戶端聊天室模型1.首先啟動聊天室服務器,使得TcpListener開始監聽端口,此時TcpListener會進入Pending狀態,等待客戶端連接;2.其次,當有客戶端連接后,通過AccepSocket返回與客戶端連接的Socket對象,然后通過讀寫Socket對象完成與聊天室客戶端的數據傳輸。聊天室客戶端成功啟動后,首先創建一個Socket對象,然后通過這個Socket對象連接聊天室服務器,連接成功后開通Socket完成數據的接收和發送處理。二、系統功能設計本設計為一個簡單的聊天室工具,設計基本的聊天功能,如聊天、列表維護等。系統主要為兩大塊:聊天室服務器及聊天室客戶端。服務器界面設計如下:客戶端界面設計如下:當客戶端連接到服務器后,服務器立刻建立一個數據接收的獨立線程。在接收線程中,如果收到了聊天命令,就對其進行解析處理。聊天室客戶端一共處理的命令有五種:OK\ERR\LIST\JOIN\QUIT命令。五、程序設計(代碼)服務器端設計:引入網絡操作命名空間System.Net、System.Net.Sockets;線程處理命名空間System.Threading第一步:界面設計及類與相關成員的定義對界面進行設計(簡單)對內部函數進行設計(要編寫一個獨立的類即Client類,封裝了客戶端的信息與連接,每一個客戶進入聊天室,就創建一個Client對象,用于保存該用戶的信息并接收用戶數據和發送信息到客戶端)幾個重要的類:TcpListener類(服務器套接字創建)、Socket類internalstaticHashtableclients=newHashtable();//clients數組保存當前在線用戶的client對象privateTcpListenerlistener;//該服務器默認的監聽端口號staticintMAX_NUM=100;//服務器可以支持的客戶端的最大連接數internalstaticboolSocketServiceFlag=false;//開始服務的標志//獲得本地局域網或者撥號動態分配的IP地址,在啟動服務器時會用到IP地址privatestringgetIPAddress(){//獲得本機局域網IP地址IPAddress[]Addresslist=Dns.GetHostEntry(Dns.GetHostName()).AddressList;if(Addresslist.Length<1){return"";}returnAddresslist[0].ToString();}//獲得動態的IP地址privatestaticstringgetDynamicIPAddress(){IPAddress[]Addresslist=Dns.GetHostEntry(Dns.GetHostName()).AddressList;if(Addresslist.Length<2){return"";}returnAddresslist[1].ToString();}//服務器監聽的端口號通過getValidPort()函數獲得privateintgetValidPort(stringport){intlport;//測試端口號是否有效try{//是否為空if(port==""){thrownewArgumentException("端口號為空,不能啟動服務器");}lport=System.Convert.ToInt32(port);}catch(Exceptione){Console.WriteLine("無效的端口號:"+e.ToString());this.rtbSocketMsg.AppendText("無效的端口號:"+e.ToString()+"\n");return-1;}returnlport;}privatevoidbtnSocketStart_Click(objectsender,EventArgse){intport=getValidPort(tbSocketPort.Text);if(port<0){return;}stringip=this.getIPAddress();try{IPAddressipAdd=IPAddress.Parse(ip);listener=newTcpListener(ipAdd,port);//創建服務器套接字listener.Start();//開始監聽服務器端口this.rtbSocketMsg.AppendText("Socket服務器已經啟動,正在監聽"+ip+"端口號:"+this.tbSocketPort.Text+"\n");//啟動一個新的線程,執行方法this.StartSocketListen,//以便在一個獨立的進程中執行確認與客戶端Socket連接的操作Form1.SocketServiceFlag=true;Threadthread=newThread(newThreadStart(this.StartSocketListen));thread.Start();this.btnSocketStart.Enabled=false;this.btnSocketStop.Enabled=true;}catch(Exceptionex){this.rtbSocketMsg.AppendText(ex.Message.ToString()+"\n");}}//在新的線程中的操作,它主要用于當接收到一個客戶端請求時,確認與客戶端的鏈接//并且立刻啟動一個新的線程來處理和該客戶端的信息交互privatevoidStartSocketListen(){while(Form1.SocketServiceFlag){try{//當接收到一個客戶端請求時,確認與客戶端的鏈接if(listener.Pending())//確認是否有掛起的連接請求{Socketsocket=listener.AcceptSocket();//接收掛起的連接請求if(clients.Count>=MAX_NUM){this.rtbSocketMsg.AppendText("已經達到了最大連接數:"+MAX_NUM+",拒絕新的鏈接\n");socket.Close();}else{//啟動一個新的線程//執行方法this.ServiceClient,處理用戶相應的請求ChatSever.Client.Clientclient=newChatSever.Client.Client(this,socket);ThreadclientService=newThread(newThreadStart(client.ServiceClient));clientService.Start();}}Thread.Sleep(200);//提高性能整體速度,原因不詳}catch(Exceptionex){this.rtbSocketMsg.AppendText(ex.Message.ToString()+"\n");}}}privatevoidtbSocketPort_TextChanged(objectsender,EventArgse){if(this.tbSocketPort.Text!=""){this.btnSocketStart.Enabled=true;}}//下面為一些界面處理函數privatevoidbtnSocketStop_Click(objectsender,EventArgse){Form1.SocketServiceFlag=false;this.btnSocketStart.Enabled=true;this.btnSocketStop.Enabled=false;}publicvoidaddUser(stringusername){this.rtbSocketMsg.AppendText(username+"已經加入\n");//將剛連接的用戶名加入到當前在線用戶列表中this.lbSocketClients.Items.Add(username);this.tbSocketClientsNum.Text=System.Convert.ToString(clients.Count);}publicvoidremoveUser(stringusername){this.rtbSocketMsg.AppendText(username+"已經離開\n");//將剛連接的用戶名加入到當前在線用戶列表中this.lbSocketClients.Items.Remove(username);this.tbSocketClientsNum.Text=System.Convert.ToString(clients.Count);}publicstringGetUserList(){stringRtn="";for(inti=0;i<lbSocketClients.Items.Count;i++){Rtn+=lbSocketClients.Items[i].ToString()+"|";}returnRtn;}publicvoidupdateUI(stringmsg){this.rtbSocketMsg.AppendText(msg+"\n");}privatevoidForm1_FormClosing(objectsender,FormClosingEventArgse){Form1.SocketServiceFlag=false;}//下面為Client類定義publicclassClient{privatestringname;//保存用戶名privateSocketcurrentSocket=null;//保存與當前用戶連接的Socket對象privatestringipAddress;//保存用戶的IP地址privateForm1server;//保存當前連接狀態//Closed--connected--closedprivatestringstate="closed";publicClient(Form1server,SocketclientSocket){this.server=server;this.currentSocket=clientSocket;ipAddress=getRemoteIPAddress();}publicstringName{get{returnname;}set{name=value;}}publicSocketCurrentSocket{get{returncurrentSocket;//ipAddress}}privatestringgetRemoteIPAddress(){return((IPEndPoint)currentSocket.RemoteEndPoint).Address.ToString();}//SendToClient()方法實現了向客戶端發送命令請求的功能privatevoidSendToClient(Clientclient,stringmsg){System.Byte[]message=System.Text.Encoding.Default.GetBytes(msg.ToCharArray());client.currentSocket.Send(message,message.Length,0);}//ServiceClient方法用于和客戶端進行數據通信,包括接收客戶端的請求//它根據不同的請求命令執行相應的操作,并將處理結果返回到客戶端//ServiceClient()函數為服務器接收客戶數據的線程主體,主要用來接收用戶發送來的數據,并處理聊天命令publicvoidServiceClient(){string[]tokens=null;byte[]buff=newbyte[1024];boolkeepConnect=true;//用循環來不斷地與客戶端進行交互,直到客戶端發出“EXIT”命令//將keepConnect職為false,退出循環,關閉連接,并中止當前線程while(keepConnect&&Form1.SocketServiceFlag){//tokens=null;try{if(currentSocket==null||currentSocket.Available<1){Thread.Sleep(300);continue;}//接收數據并存入BUFF數組中intlen=currentSocket.Receive(buff);//將字符數組轉化為字符串stringclientCommand=System.Text.Encoding.Default.GetString(buff,0,len);//tokens【0】中保存了命令標志符(CONNCHATPRIVLIST或EXIT)tokens=clientCommand.Split(newchar[]{'|'});if(tokens==null){Thread.Sleep(200);continue;}}catch(Exceptione){server.updateUI("發送異常:"+e.ToString());}}//以上代碼主要用于服務器初始化和接收客戶端發送來的數據。它在對用戶數據進行解析后,把用戶命令轉換為數組方式。if(tokens[0]=="CONN"){//此時接收到的命令格式化為命令標識符CONN|發送者的用戶名|tokens[1]中保存了發送者的用戶名=tokens[1];if(Form1.clients.Contains()){SendToClient(this,"ERR|User"++"已經存在");}else{HashtablesyncClients=Hashtable.Synchronized(Form1.clients);syncClients.Add(,this);//更新界面server.addUser();//對每一個當前在線的用戶發送JOIN消息命令和LIST消息命令,以此來跟新客戶端的當前在線用戶列表System.Collections.IEnumeratormyEnumerator=Form1.clients.Values.GetEnumerator();while(myEnumerator.MoveNext()){Clientclient=(Client)myEnumerator.Current;SendToClient(client,"JOIN|"+tokens[1]+"|");Thread.Sleep(100);}//更新狀態state="connected";SendToClient(this,"OK");//向客戶端發送LIST命令,以此更新客戶端的當前在線用戶列表stringmsgUsers="LIST|"+server.GetUserList();SendToClient(this,msgUsers);}}elseif(tokens[0]=="CHAT"){if(state=="connected"){//此時收到的命令的格式為:命令標識符CHAT|發送者的用戶名:發送內容|向所有當前在線的用戶轉發此信息System.Collections.IEnumeratormyEnumerator=Form1.clients.Values.GetEnumerator();while(myEnumerator.MoveNext()){Clientclient=(Client)myEnumerator.Current;//將發送者的用戶名:發送內容轉發給用戶SendToClient(client,tokens[1]);}server.updateUI(tokens[1]);}else{//senderrtoserverSendToClient(this,"ERR|stateerror,pleaseloginfirst");}}elseif(tokens[0]=="PRIV"){if(state=="connected"){//此時收到的命令的格式為:命令標識符PRIV|發送者的用戶名:發送內容|//tokens[1]中保存了發生者的用戶名stringsender=tokens[1];//tokens[2]中保存了發送者的用戶名stringreceiver=tokens[2];//tokens[3]中保存了發送的內容stringcontent=tokens[3];stringmessage=sender+"-->"+receiver+”:”+content;//僅將信息轉發給法送者和接收者if(Form1.clients.Contains(sender)){SendToClient((Client)Form1.clients[sender],message);}if(Form1.clients.Contains(receiver)){SendToClient((Client)Form1.clients[receiver],message);}server.updateUI(tokens[1]);}else{//senderrtoserverSendToClient(this,"ERR|stateerror,pleaseloginfirst");}}elseif(tokens[0]=="EXIT"){//此時收到的命令的格式為:命令標識符EXIT|發送者的用戶名:發送內容|//向所有當前在線的用戶發送該用戶已離開的消息if(Form1.clients.Contains(tokens[1])){Clientclient=(Client)Form1.clients(tokens[1]));//將該用戶對應Client對象從clients中刪除HashtablesyncClients=Hashtable.Synchronized(Form1.clients);syncClients.Remove();server.removeUser();//向客戶端發送QUIT命令stringmessage="QUIT|"+tokens[1];System.Collections.IEnumeratormyEnumerator=Form1.clients.Values.GetEnumerator();while(myEnumerator.MoveNext()){Clientc=(Client)myEnumerator.Current;//將發送者的用戶名:發送內容轉發給用戶SendToClient(c,message);}server.updateUI("QUIT");}//退出當前線程break;}Thread.Sleep(200);}客戶端設計:包含一個類ChatClientForm,該類封裝了聊天室客戶端界面和聊天命令處理邏輯。其中一個重要的類TcpClient類(用于與服務器的連接)TcpClienttcpClient;//與服務器的鏈接privateNetworkStreamStream;//與服務器數據交互的流通道privatestaticstringCLOSED="closed";privatestaticstringCONNECTED="connected";privatestringstate=CLOSED;privateboolstopFlag;privateColorcolor;//保存當前客戶端顯示的顏色//連接聊天室服務器//通過TcpClient方法連接聊天室服務器并發送CONN消息命令privatevoidbtnLogin_Click_1(objectsender,EventArgse){if(state==CONNECTED){return;}if(this.tbUserName.TextLength==0){MessageBox.Show("請輸入您的昵稱!","提示信息",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);this.tbUserName.Focus();//為控件設置焦點,人性化設計return;}try{//創建一個客戶端套接字,它是Login的一個公共屬性tcpClient=newTcpClient();//將被傳遞給ChatClient窗體tcpClient.Connect(IPAddress.Parse(txtHost.Text),Int32.Parse(txtPort.Text));//向指定的IP地址服務器發出連接請求Stream=tcpClient.GetStream();//獲得與服務器數據交互的流通道NetworksStream//啟動一個新的線程,執行方法this.ServerResponse(),以便來響應從服務器發回的信息Threadthread1=newThread(newThreadStart(this.ServerResponse));thread1.Start();//向服務器發送CONN請求命令//此命令的格式與服務器端的定義的格式一致//命令格式為:命令標志符CONN|發送者的用戶名stringcmd="CONN|"+this.tbUserName.Text+"|";//將字符串轉化為字符數組Byte[]outbytes=System.Text.Encoding.Default.GetBytes(cmd.ToCharArray());Stream.Write(outbytes,0,outbytes.Length);}catch(Exceptionex){MessageBox.Show(ex.Message);}}privatevoidbtnSend_Click_1(objectsender,EventArgse){try{if(!this.cbPrivate.Checked){//此時命令的格式是:命令標識符CHAT|發送者的用戶名:發送內容|stringmessage="CHAT|"+this.tbUserName.Text+":"+tbSendContent.Text;tbSendContent.Text="";tbSendContent.Focus();byte[]outbytes=System.Text.Encoding.Default.GetBytes(message.ToCharArray());//將字符串轉化為字符數組Stream.Write(outbytes,0,outbytes.Length);}else{if(lstUsers.SelectedIndex==-1){MessageBox.Show("請在列表中選擇一個用戶","提示信息",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);return;}stringreceiver=lstUsers.SelectedItem.ToString();//消息的格式是:命令標識符PRIV|發送者的用戶名|接收者的用戶名|發送內容stringmessage="PRIV|{"+this.tbUserName.Text+"|"+receiver+"|"+tbSendContent.Text+"|";tbSendContent.Text="";tbSendContent.Focus();byte[]outbytes=System.Text.Encoding.Default.GetBytes(message.ToCharArray());//將字符串轉化為字符數組Stream.Write(outbytes,0,outbytes.Length);}}catch{this.rtbMsg.AppendText("網絡發生錯誤!");}}//this.ServerResponse()方法用于接收從服務器發回的信息,根據不同的命令,執行相應的操作privatevoidServerResponse(){//定義一個byte數組,用于接收從服務器端發來的數據//每次所能接受的數據包的最大長度為1024個字節byte[]buff=newbyte[1024];stringmsg;intlen;try{if(Stream.CanRead==false){return;}stopFlag=false;while(!stopFlag){//從流中得到數據,并存入到buff字符數組中len=Stream.Read(buff,0,buff.Length);if(len<1){Thread.Sleep(200);continue;}//將字符數組轉化為字符串msg=System.Text.Encoding.Default.GetString(buff,0,len);msg.Trim();string[]tokens=msg.Split(newchar[]{'|'});//tokens[0]中保存了命令標志符LISTJOINQUITif(tokens[0].ToUpper()=="OK"){//處理響應add("命令執行成功!");}elseif(tokens[0].ToUpper()=="ERR"){add("命令執行錯誤:"+tokens[1]);}elseif(tokens[0]=="LIST"){//此時從服務器返回的消息格式:命令標志符LIST|用戶名1|用戶名2|。。(所有在線用戶名)//add(“獲得用戶列表”),更新在線用戶列表lstUsers.Items.Clear();for(inti=1;i<tokens.Length-1;i++){lstUsers.Items.Add(tokens[i].Trim());}}elseif(tokens[0]=="JOIN"){//此時從服務器返回的消息格式:命令標志符JOIN|剛剛登入的用戶名add(tokens[1]+"+已經進入了聊天室");this.lstUsers.Items.Add(tokens[1]);if(this.tbUserName.Text==tokens[1]){this.state=CONNECTED;}}elseif(tokens[0]=="QUIT"){if(this.lstUsers.Items.IndexOf(tokens[1])>-1){this.lstUsers.Items.Remove(tokens[1]);}add("用戶:"+tokens[1]+"已經離開");}else{//如果從服務器返回的其他消息格式,則在ListBox控件中直接顯示//this.rtbMsg.SelectedText=msg+"\n";add(msg);}}//關閉連接tcpClient.Close();}catch{add("網絡發生錯誤");}}//設置字體顏色//向顯示消息的rtbMsg中添加信息是通過add函數完成的privatevoidadd(stringmsg){if(!color.IsEmpty){this.rtbMsg.SelectionColor=color;}this.rtbMsg.SelectedText=msg+"\n";}privatevoidbtnExit_Click_1(objectsender,EventArgse){if(true){stringmessage="EXIT|"+this.tbUserName.Text+"|";//將字符串轉化為字符數組byte[]outbytes=System.Text.Encoding.Default.GetBytes(message.ToCharArray());Stream.Write(outbytes,0,outbytes.Length);this.state=CLOSED;this.stopFlag=true;this.lstUsers.Items.Clear();}}//將“EXIT”命令發送給服務器,此命令格式要與服務器端的命令格式一致privatevoidForm1_FormClosing(objectsender,FormClosingEventArgse){//btnExit_Click_1(sender,e);btnExit_Click_1(sender,e);}privatevoidbtnColor_Click(objectsender,EventArgse){ColorDialogcolorDialog1=newColorDialog();colorDialog1.Color=this.rtbMsg.SelectionColor;if(colorDialog1.ShowDialog()==System.Windows.Forms.DialogResult.OK&&colorDialog1.Color!=this.rtbMsg.SelectionColor){this.rtbMsg.SelectionColor=colorDialog1.Color;color=colorDialog1.Color;}}privatevoidbtnSend_Click_1(objectsender,EventArgse){try{if(!this.cbPrivate.Checked){//此時命令的格式是:命令標識符CHAT|發送者的用戶名:發送內容|stringmessage="CHAT|"+this.tbUserName.Text+":"+tbSendContent.Text;tbSendContent.Text="";tbSendContent.Focus();byte[]outbytes=System.Text.Encoding.Default.GetBytes(message.ToCharArray());//將字符串轉化為字符數組Stream.Write(outbytes,0,outbytes.Length);}else{if(lstUsers.SelectedIndex==-1){MessageBox.Show("請在列表中選擇一個用戶","提示信息",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);return;}stringreceiver=lstUsers.SelectedItem.ToString();//消息的格式是:命令標識符PRIV|發送者的用戶名|接收者的用戶名|發送內容stringmessage="PRIV|{"+this.tbUserName.Text+"|"+receiver+"|"+tbSendContent.Text+"|";tbSendContent.Text="";tbSendContent.Focus();byte[]outbytes=System.Text.Encoding.Default.GetBytes(message.ToCharArray());//將字符串轉化為字符數組Stream.Write(outbytes,0,outbytes.Length);}}catch{this.rtbMsg.AppendText("網絡發生錯誤!");}}//this.ServerResponse()方法用于接收從服務器發回的信息,根據不同的命令,執行相應的操作privatevoidServerResponse(){//定義一個byte數組,用于接收從服務器端發來的數據//每次所能接受的數據包的最大長度為1024個字節byte[]buff=newbyte[1024];stringmsg;intlen;try{if(Stream.CanRead==false){return;}stopFlag=false;while(!stopFlag){//從流中得到數據,并存入到buff字符數組中len=Stream.Read(buff,0,buff.Length);if(len<1){Thread.Sleep(200);continue;}//將字符數組轉化為字符串msg=System.Text.Encoding.Default.GetString(buff,0,len);msg.Trim();string[]tokens=msg.Split(newchar[]{'|'});//分離字符//tokens[0]中保存了命令標志符LISTJOINQUITif(tokens[0].ToUpper()=="OK"){//處理響應add("命令執行成功!");}elseif(tokens[0].ToUpper()=="ERR"){add("命令執行錯誤:"+tokens[1]);}elseif(tokens[0]=="LIST"){//此時從服務器返回的消息格式:命令標志符LIST|用戶名1|用戶名2|。。(所有在線用戶名)//add(“獲得用戶列表”),更新在線用戶列表lstUsers.Items.Clear();for(inti=1;i<tokens.Length-1;i++){

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論