基於C# Socket實現多人網絡聊天室


首先不多說,最終實現界面如下,可以通過點擊啟動服務,開啟TCP服務器:

 

 

開啟TCP服務器之后,可以通過點擊客戶端,打開一個獨立的TCP客戶端,打開客戶端之后,輸入正確的IP地址和端口號,可以進行連接服務器,這里可以同時開啟多個客戶端:

 

 

每個客戶端連接成功后,服務器的列表中會多出一個EndPoint,連接成功后,服務器和客戶端之間就可以自由通話了,可以發送消息,也可以發送文件。

其實這就是QQ等即時通信工具的雛形,如果兩個客戶端之間需要通信,就通過服務器進行中轉。

由於服務器代碼量較大,下面上傳一下客戶端的代碼,僅供參考:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.IO;
  7 using System.Linq;
  8 using System.Net;
  9 using System.Net.Sockets;
 10 using System.Text;
 11 using System.Threading;
 12 using System.Threading.Tasks;
 13 using System.Windows.Forms;
 14 
 15 namespace MyTCPServer
 16 {
 17     delegate void FileSaveDelegate(byte[] bt,int length);
 18 
 19     public partial class FrmClient : Form
 20     {
 21         public FrmClient()
 22         {
 23             InitializeComponent();
 24 
 25             //委托對象綁定方法
 26             MyShowMsg += ShowMsg;
 27 
 28             MyFileSave += FileSave;
 29         }
 30 
 31         //運行標志位
 32         private bool IsRun = true;
 33 
 34         //創建連接服務器的Socket
 35         Socket sockClient = null;
 36 
 37         //創建接收服務器消息的線程
 38         Thread thrClient = null;
 39 
 40         //創建委托對象
 41         ShowMsgDelegate MyShowMsg;
 42 
 43         FileSaveDelegate MyFileSave;
 44 
 45         private void btnConnect_Click(object sender, EventArgs e)
 46         {
 47             //獲取IP對象
 48             IPAddress address = IPAddress.Parse(this.txtIp.Text.Trim());
 49 
 50             //根據IP對象和端口號創建網絡節點對象
 51             IPEndPoint endPoint = new IPEndPoint(address, int.Parse(this.txtPort.Text.Trim()));
 52 
 53             //創建負責連接的套接字,注意其中參數:[IPV4地址,字節流,TCP協議]
 54             sockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 55 
 56             try
 57             {
 58                 Invoke(MyShowMsg, "與服務器連接中......");
 59                 sockClient.Connect(endPoint);
 60             }
 61             catch (Exception ex)
 62             {
 63                 MessageBox.Show("建立連接失敗:" + ex.Message, "建立連接");
 64                 return;
 65             }
 66 
 67             Invoke(MyShowMsg, "與服務器連接成功!");
 68 
 69             thrClient = new Thread(ReceiveMsg);
 70             thrClient.IsBackground = true;
 71             thrClient.Start();
 72 
 73             this.btnConnect.Enabled = false;
 74         }
 75 
 76         private void FileSave(byte[] arrMsgRec, int length)
 77         {
 78             try
 79             {
 80                 SaveFileDialog sfd = new SaveFileDialog();
 81 
 82                 if (sfd.ShowDialog(this) == DialogResult.OK)
 83                 {
 84 
 85                     string fileSavePath = sfd.FileName;// 獲得文件保存的路徑;
 86                     // 創建文件流,然后根據路徑創建文件;
 87                     using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
 88                     {
 89                         fs.Write(arrMsgRec, 1, length - 1);
 90                         Invoke(MyShowMsg, "文件保存成功:" + fileSavePath);
 91                     }
 92                 }
 93             }
 94             catch (Exception ex)
 95             {
 96                 MessageBox.Show(ex.Message);
 97             }
 98 
 99         }
100 
101         private void ReceiveMsg()
102         {
103            while(IsRun)
104             {
105                 // 定義一個2M的緩存區
106                 byte[] arrMsgRec = new byte[1024 * 1024 * 2];
107 
108                 // 將接受到的數據存入到輸入 arrMsgRec中
109                 int length = -1;
110                 try
111                 {
112                     length = sockClient.Receive(arrMsgRec); // 接收數據,並返回數據的長度;
113                 }
114                 catch (SocketException)
115                 {
116                     return;
117                 }
118                 catch (Exception e)
119                 {
120                     Invoke(MyShowMsg, "連接斷開:" + e.Message);
121                     return;
122                 }
123                 if (arrMsgRec[0] == 0) // 表示接收到的是消息數據;
124                 {
125                     string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);// 將接受到的字節數據轉化成字符串;
126                     Invoke(MyShowMsg, strMsg);
127                 }
128                 if (arrMsgRec[0] == 1) // 表示接收到的是文件數據;
129                 {
130                     Invoke(MyFileSave, arrMsgRec, length);
131                 }
132             }
133         }
134 
135         private void ShowMsg(string str)
136         {
137             txtMsg?.AppendText(str + Environment.NewLine);
138         }
139 
140         private void btnSend_Click(object sender, EventArgs e)
141         {
142             string strMsg = txt_Name.Text.Trim() + Environment.NewLine + "    -->" + txtMsgSend.Text.Trim() + Environment.NewLine;
143             byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
144             byte[] arrSendMsg = new byte[arrMsg.Length + 1];
145             arrSendMsg[0] = 0; // 用來表示發送的是消息數據
146             Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
147             sockClient.Send(arrSendMsg); // 發送消息;
148             Invoke(MyShowMsg, strMsg);
149             txtMsgSend.Clear();
150         }
151 
152         private void btnSendFile_Click(object sender, EventArgs e)
153         {
154             if (string.IsNullOrEmpty(txtSelectFile.Text))
155             {
156                 MessageBox.Show("請選擇要發送的文件!!!");
157             }
158             else
159             {
160                 // 用文件流打開用戶要發送的文件;
161                 using (FileStream fs = new FileStream(txtSelectFile.Text, FileMode.Open))
162                 {
163                     //在發送文件以前先給好友發送這個文件的名字+擴展名,方便后面的保存操作;
164                     string fileName = System.IO.Path.GetFileName(txtSelectFile.Text);
165                     string fileExtension = System.IO.Path.GetExtension(txtSelectFile.Text);
166                     string strMsg = "發送的文件為: " + fileName + Environment.NewLine;
167                     byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
168                     byte[] arrSendMsg = new byte[arrMsg.Length + 1];
169                     arrSendMsg[0] = 0; // 用來表示發送的是消息數據
170                     Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
171                     sockClient.Send(arrSendMsg); // 發送消息;
172 
173                     byte[] arrFile = new byte[1024 * 1024 * 2];
174                     int length = fs.Read(arrFile, 0, arrFile.Length);  // 將文件中的數據讀到arrFile數組中;
175                     byte[] arrFileSend = new byte[length + 1];
176                     arrFileSend[0] = 1; // 用來表示發送的是文件數據;
177                     Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length);
178                     // 還有一個 CopyTo的方法,但是在這里不適合; 當然還可以用for循環自己轉化;
179                     sockClient.Send(arrFileSend);// 發送數據到服務端;
180                     txtSelectFile.Clear();
181                 }
182             }
183         }
184 
185         private void btnSelectFile_Click(object sender, EventArgs e)
186         {
187             OpenFileDialog ofd = new OpenFileDialog();
188             ofd.InitialDirectory = "D:\\";
189             if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
190             {
191                 txtSelectFile.Text = ofd.FileName;
192             }
193         }
194 
195         private void FrmClient_FormClosing(object sender, FormClosingEventArgs e)
196         {
197             IsRun  = false;
198             sockClient?.Close();
199         }
200     }
201 }

  如果大家還有什么不明白的地方,可以關注一下微信公眾號:dotNet工控上位機


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM