C# Socket服务端与客户端通信(包含大文件的断点传输)
步驟:
一、服務端的建立
1.服務端的項目建立以及頁面布局
2.各功能按鍵的事件代碼
1)傳輸類型說明以及全局變量
2)Socket通信服務端具體步驟:
? (1)建立一個Socket
? (2)接收信息
? (3)發送數據(這里分發送字符串、文件(包含大文件)、震動)
二、客戶端的建立
1.服務端的項目建立以及頁面布局
2.各功能按鍵的事件代碼
1)傳輸類型說明以及全局變量
2)Socket通信服務端具體步驟:
? (1)建立一個Socket
? (2)接收信息
? (3)發送數據(這里分發送字符串、文件(包含大文件)、震動)
?
?
注意:此圖是Socket通信的精華,在使用Socket通信時,有什么迷惑的可以看看此圖,下面我們講解的時候也是參照此圖
?
Socket大家肯定很熟悉,對已內部的通信邏輯,肯定也有一定得了解---
對于Socket研究了兩天寫了一個小程序,通過Socket服務端與客戶端的通信,以及大文件之間斷點的傳輸(這里只做了服務端給客戶端傳送大文件,如果想把客戶端的大文件傳送給服務端也是一樣的道理,看了文章,大家肯定可以自己實現)······
(自己才疏學淺,如有bug請諒解,但功能還是能實現的)
下面根據步驟進入正題:
一、服務端的建立
1.服務端的項目建立以及頁面布局
新建解決方案“Socket通信”以及兩個Winform項目(1)SockeClient——客戶端 ? ?(2)SocketServer——服務器
給服務端界面布局——參照上圖(這個大家肯定都是手到擒來就不累贅了······)
2.各功能按鍵的事件代碼
先把整個服務端的代碼貼出來,然后我們在一一講解
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | namespace?SocketServer { ????public?partial?class?Form1 : Form ????{ ? ????????//說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息 ????????// 0:表示傳遞的是字符串信息 ????????// 1:表示傳遞的是文件信息 ????????// 2:表示的是震動 ? ????????/// <summary> ????????/// 用來存放連接服務的客戶端的IP地址和端口號,對應的Socket ????????/// </summary> ????????Dictionary<string, Socket> dicSocket =?new?Dictionary<string, Socket>(); ? ????????public?Form1() ????????{ ????????????InitializeComponent(); ????????} ? ????????private?void?Form1_Load(object?sender, EventArgs e) ????????{ ????????????//不檢測跨線程之間的空間調用 ????????????Control.CheckForIllegalCrossThreadCalls =?false; ????????} ? ????????/// <summary> ????????/// 開啟監聽 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnStart_Click(object?sender, EventArgs e) ????????{ ????????????try ????????????{ ????????????????//當點擊開始監聽的時候 在服務器端創建一個負責監IP地址跟端口號的Socket ????????????????Socket socketWatch =?new?Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ????????????????//獲取IP ????????????????IPAddress ip = IPAddress.Any; ????????????????//創建端口號 ????????????????IPEndPoint port =?new?IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); ????????????????//監聽 ????????????????socketWatch.Bind(port); ????????????????ShowMsg("監聽成功"); ????????????????socketWatch.Listen(10); ????????????????//新建線程,去接收客戶端發來的信息 ????????????????Thread td =?new?Thread(AcceptMgs); ????????????????td.IsBackground =?true; ????????????????td.Start(socketWatch); ????????????} ????????????catch ????????????{ ????????????????? ????????????}?????????? ????????} ? ????????/// <summary> ????????/// 接收客戶端發送的信息 ????????/// </summary> ????????/// <param name="o"></param> ????????private?void?AcceptMgs(object?o) ????????{ ????????????try ????????????{ ????????????????Socket socketWatc = (Socket)o; ????????????????while?(true) ????????????????{ ????????????????????負責跟客戶端通信的Socket ????????????????????Socket socketSend = socketWatc.Accept(); ????????????????????//將遠程連接的客戶端的IP地址和Socket存入集合中 ????????????????????dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend); ????????????????????//將遠程連接的客戶端的IP地址和端口號存儲下拉框中 ????????????????????cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString()); ????????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": 連接成功"); ????????????????????//新建線程循環接收客戶端發來的信息 ????????????????????Thread td =?new?Thread(Recive); ????????????????????td.IsBackground =?true; ????????????????????td.Start(socketSend); ????????????????} ????????????} ????????????catch?{ } ????????????? ????????} ? ????????/// <summary> ????????/// 接收客戶端發來的數據,并顯示出來 ????????/// </summary> ????????private?void?Recive(object?o) ????????{ ????????????Socket socketSend = (Socket)o; ????????????try ????????????{ ????????????????while?(true) ????????????????{ ????????????????????//客戶端連接成功后,服務器應該接受客戶端發來的消息 ????????????????????? ????????????????????if?(socketSend ==?null) ????????????????????{ ????????????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????????????continue; ????????????????????} ????????????????????byte[] buffer =?new?byte[1024 * 1024 * 2]; ????????????????????//實際接受到的有效字節數 ????????????????????int?r = socketSend.Receive(buffer); ????????????????????//如果客戶端關閉,發送的數據就為空,然后就跳出循環 ????????????????????if?(r == 0) ????????????????????{ ????????????????????????break; ????????????????????}?????????????????? ????????????????????if?(buffer[0] == 0)?//如果接收的字節數組的第一個字節是0,說明接收的字符串信息 ????????????????????{ ????????????????????????string?strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ????????????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": "?+ strMsg); ????????????????????} ????????????????????else?if?(buffer[0] == 1)?//如果接收的字節數組的第一個字節是1,說明接收的是文件 ????????????????????{ ????????????????????????string?filePath =?""; ????????????????????????SaveFileDialog sfd =?new?SaveFileDialog(); ????????????????????????sfd.Title =?"保存文件"; ????????????????????????sfd.InitialDirectory =?@"C:\Users\Administrator\Desktop"; ????????????????????????sfd.Filter =?"文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ????????????????????????//如果沒有選擇保存文件路徑就一直打開保存框 ????????????????????????while?(true) ????????????????????????{ ????????????????????????????sfd.ShowDialog(this); ????????????????????????????filePath = sfd.FileName; ????????????????????????????if?(string.IsNullOrEmpty(filePath)) ????????????????????????????{ ????????????????????????????????continue; ????????????????????????????} ????????????????????????????else ????????????????????????????{ ????????????????????????????????break; ????????????????????????????} ????????????????????????} ????????????????????????//保存接收的文件 ????????????????????????using?(FileStream fsWrite =?new?FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write)) ????????????????????????{ ????????????????????????????fsWrite.Write(buffer, 1, r - 1); ????????????????????????} ????????????????????????ShowMsg(socketSend.RemoteEndPoint +?": 接收文件成功"); ????????????????????????? ????????????????????} ????????????????????else?if?(buffer[0] == 2)?//如果接收的字節數組的第一個字節是2,說明接收的是震動 ????????????????????{ ????????????????????????ZD(); ????????????????????} ????????????????} ????????????} ????????????catch{}????????? ????????} ? ? ????????/// <summary> ????????/// 顯示信息 ????????/// </summary> ????????/// <param name="message"></param> ????????private?void?ShowMsg(string?message) ????????{ ????????????txtLog.AppendText(message +?"\r\n"); ????????} ? ????????/// <summary> ????????/// 發送信息 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSend_Click(object?sender, EventArgs e) ????????{ ????????????? ????????????//獲得選中客戶端ip對應的通信Socket?????? ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????string?strSend=txtMsg.Text; ????????????try ????????????{ ????????????????byte[] buffer = Encoding.UTF8.GetBytes(strSend); ????????????????//獲得發送的信息時候,在數組前面加上一個字節 0 ????????????????List<byte> list =?new?List<byte>(); ????????????????list.Add(0); ????????????????list.AddRange(buffer); ????????????????//將泛型集合轉換為數組 ????????????????byte[] newBuffer = list.ToArray(); ????????????????//將了標識字符的字節數組傳遞給客戶端 ????????????????socketSend.Send(newBuffer); ????????????????txtMsg.Text =?""; ????????????} ????????????catch ????????????{ ????????????}??????????? ????????} ? ????????/// <summary> ????????/// 選擇文件 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSelect_Click(object?sender, EventArgs e) ????????{ ????????????//打開文件 ????????????OpenFileDialog ofd =?new?OpenFileDialog(); ????????????ofd.Title =?"選擇要傳的文件"; ????????????ofd.InitialDirectory =?@"C:\Users\Administrator\Desktop"; ????????????ofd.Filter =?"文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ????????????ofd.ShowDialog(); ????????????//得到選擇文件的路徑 ????????????txtPath.Text = ofd.FileName; ????????} ? ????????/// <summary> ????????/// 發送文件 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSendFile_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????string?filePath = txtPath.Text; ????????????if?(string.IsNullOrEmpty(filePath)) ????????????{ ????????????????MessageBox.Show("請選擇文件"); ????????????????return; ????????????} ????????????Thread td =?new?Thread(SendBigFile); ????????????td.IsBackground =?true; ????????????td.Start(); ????????????? ????????} ? ????????/// <summary> ????????/// 大文件斷點傳送 ????????/// </summary> ????????private?void?SendBigFile() ????????{ ????????????string?filePath = txtPath.Text; ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????try ????????????{ ????????????????//讀取選擇的文件 ????????????????using?(FileStream fsRead =?new?FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)) ????????????????{ ????????????????????//1. 第一步:發送一個包,表示文件的長度,讓客戶端知道后續要接收幾個包來重新組織成一個文件 ????????????????????long?length = fsRead.Length; ????????????????????byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString()); ????????????????????//獲得發送的信息時候,在數組前面加上一個字節 1 ????????????????????List<byte> list =?new?List<byte>(); ????????????????????list.Add(1); ????????????????????list.AddRange(byteLength); ????????????????????socketSend.Send(list.ToArray());?// ????????????????????//2. 第二步:每次發送一個1MB的包,如果文件較大,則會拆分為多個包 ????????????????????byte[] buffer =?new?byte[1024 * 1024]; ????????????????????long?send = 0;?//發送的字節數?????????????????? ????????????????????while?(true)??//大文件斷點多次傳輸 ????????????????????{ ????????????????????????int?r = fsRead.Read(buffer, 0, buffer.Length); ????????????????????????if?(r == 0) ????????????????????????{ ????????????????????????????break; ????????????????????????} ????????????????????????socketSend.Send(buffer, 0, r, SocketFlags.None); ????????????????????????send += r; ????????????????????????ShowMsg(string.Format("{0}: 已發送:{1}/{2}", socketSend.RemoteEndPoint, send, length)); ????????????????????} ????????????????????ShowMsg("發送完成"); ????????????????????txtPath.Text =?""; ????????????????} ????????????} ????????????catch ????????????{ ? ????????????} ????????} ? ????????/// <summary> ????????/// 震動 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnZD_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????try ????????????{ ????????????????// 首字節是2說明是震動 ????????????????byte[] buffer =?new?byte[1]; ????????????????buffer[0] = 2; ????????????????socketSend.Send(buffer); ????????????} ????????????catch ????????????{ ????????????????? ????????????} ????????????? ????????} ? ????????/// <summary> ????????/// 震動 ????????/// </summary> ????????private?void?ZD() ????????{ ????????????//獲取當前窗體的坐標 ????????????Point point =?this.Location; ????????????//反復給窗體坐標復制一百次,達到震動的效果 ????????????for?(int?i = 0; i < 100; i++) ????????????{ ????????????????this.Location =?new?Point(point.X - 5, point.Y - 5); ????????????????this.Location =?new?Point(point.X + 5, point.Y + 5); ????????????} ????????????this.Location = point; ????????} ????} } |
1)傳輸類型說明以及全局變量
這些說明以及全局變量,說的也比較清楚,也不累贅了。
2)Socket通信服務端具體步驟:
(這些步驟都是根據第一個圖來的)
?(1)建立一個Socket
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /// <summary> ????????/// 開啟監聽 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnStart_Click(object?sender, EventArgs e) ????????{ ????????????try ????????????{ ????????????????//當點擊開始監聽的時候 在服務器端創建一個負責監IP地址跟端口號的Socket ????????????????Socket socketWatch =?new?Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ????????????????//獲取IP ????????????????IPAddress ip = IPAddress.Any; ????????????????//創建端口號 ????????????????IPEndPoint port =?new?IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); ????????????????//監聽 ????????????????socketWatch.Bind(port); ????????????????ShowMsg("監聽成功"); ????????????????socketWatch.Listen(10); ????????????????//新建線程,去接收客戶端發來的信息 ????????????????Thread td =?new?Thread(AcceptMgs); ????????????????td.IsBackground =?true; ????????????????td.Start(socketWatch); ????????????} ????????????catch ????????????{ ????????????????? ????????????}?????????? ????????} |
在開啟監聽按鈕里,我們建立了Socket,以及監聽的最大客戶端數?socketWatch.Listen(10)
由于服務端會不停的去監視接收客戶端發來的信息,如果把這個工作放到主線程里,程序會出現假死的現象,所以這里給他放到一個新的線程里。
(2)接收信息
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /// <summary> ????????/// 接收客戶端發送的信息 ????????/// </summary> ????????/// <param name="o"></param> ????????private?void?AcceptMgs(object?o) ????????{ ????????????try ????????????{ ????????????????Socket socketWatc = (Socket)o; ????????????????while?(true) ????????????????{ ????????????????????負責跟客戶端通信的Socket ????????????????????Socket socketSend = socketWatc.Accept(); ????????????????????//將遠程連接的客戶端的IP地址和Socket存入集合中 ????????????????????dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend); ????????????????????//將遠程連接的客戶端的IP地址和端口號存儲下拉框中 ????????????????????cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString()); ????????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": 連接成功"); ????????????????????//新建線程循環接收客戶端發來的信息 ????????????????????Thread td =?new?Thread(Recive); ????????????????????td.IsBackground =?true; ????????????????????td.Start(socketSend); ????????????????} ????????????} ????????????catch?{ } ????????????? ????????} |
接收信息是會根據接收到字節數字的第一個字節來判斷接收到的是什么
這個在方法Recive里進行判斷
| 1 | /// <summary> |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | /// 接收客戶端發來的數據,并顯示出來 /// </summary> private?void?Recive(object?o) { ????Socket socketSend = (Socket)o; ????try ????{ ????????while?(true) ????????{ ????????????//客戶端連接成功后,服務器應該接受客戶端發來的消息 ????????????? ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????continue; ????????????} ????????????byte[] buffer =?new?byte[1024 * 1024 * 2]; ????????????//實際接受到的有效字節數 ????????????int?r = socketSend.Receive(buffer); ????????????//如果客戶端關閉,發送的數據就為空,然后就跳出循環 ????????????if?(r == 0) ????????????{ ????????????????break; ????????????}?????????????????? ????????????if?(buffer[0] == 0)?//如果接收的字節數組的第一個字節是0,說明接收的字符串信息 ????????????{ ????????????????string?strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": "?+ strMsg); ????????????} ????????????else?if?(buffer[0] == 1)?//如果接收的字節數組的第一個字節是1,說明接收的是文件 ????????????{ ????????????????string?filePath =?""; ????????????????SaveFileDialog sfd =?new?SaveFileDialog(); ????????????????sfd.Title =?"保存文件"; ????????????????sfd.InitialDirectory =?@"C:\Users\Administrator\Desktop"; ????????????????sfd.Filter =?"文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ????????????????//如果沒有選擇保存文件路徑就一直打開保存框 ????????????????while?(true) ????????????????{ ????????????????????sfd.ShowDialog(this); ????????????????????filePath = sfd.FileName; ????????????????????if?(string.IsNullOrEmpty(filePath)) ????????????????????{ ????????????????????????continue; ????????????????????} ????????????????????else ????????????????????{ ????????????????????????break; ????????????????????} ????????????????} ????????????????//保存接收的文件 ????????????????using?(FileStream fsWrite =?new?FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write)) ????????????????{ ????????????????????fsWrite.Write(buffer, 1, r - 1); ????????????????} ????????????????ShowMsg(socketSend.RemoteEndPoint +?": 接收文件成功"); ????????????????? ????????????} ????????????else?if?(buffer[0] == 2)?//如果接收的字節數組的第一個字節是2,說明接收的是震動 ????????????{ ????????????????ZD(); ????????????} ????????} ????} ????catch{}????????? } |
(3)發送數據(這里分發送字符串、文件(包含大文件)、震動)
發送字符串信息
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /// <summary> ????????/// 發送信息 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSend_Click(object?sender, EventArgs e) ????????{ ????????????? ????????????//獲得選中客戶端ip對應的通信Socket?????? ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????string?strSend=txtMsg.Text; ????????????try ????????????{ ????????????????byte[] buffer = Encoding.UTF8.GetBytes(strSend); ????????????????//獲得發送的信息時候,在數組前面加上一個字節 0 ????????????????List<byte> list =?new?List<byte>(); ????????????????list.Add(0); ????????????????list.AddRange(buffer); ????????????????//將泛型集合轉換為數組 ????????????????byte[] newBuffer = list.ToArray(); ????????????????//將了標識字符的字節數組傳遞給客戶端 ????????????????socketSend.Send(newBuffer); ????????????????txtMsg.Text =?""; ????????????} ????????????catch ????????????{ ????????????}??????????? ????????} |
發送震動
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /// <summary> ????????/// 震動 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnZD_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????try ????????????{ ????????????????// 首字節是2說明是震動 ????????????????byte[] buffer =?new?byte[1]; ????????????????buffer[0] = 2; ????????????????socketSend.Send(buffer); ????????????} ????????????catch ????????????{ ????????????????? ????????????} ????????????? ????????} ? ????????/// <summary> ????????/// 震動 ????????/// </summary> ????????private?void?ZD() ????????{ ????????????//獲取當前窗體的坐標 ????????????Point point =?this.Location; ????????????//反復給窗體坐標復制一百次,達到震動的效果 ????????????for?(int?i = 0; i < 100; i++) ????????????{ ????????????????this.Location =?new?Point(point.X - 5, point.Y - 5); ????????????????this.Location =?new?Point(point.X + 5, point.Y + 5); ????????????} ????????????this.Location = point; ????????} |
發送文件(包含大文件)
首先要選擇文件
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /// <summary> ????????/// 選擇文件 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSelect_Click(object?sender, EventArgs e) ????????{ ????????????//打開文件 ????????????OpenFileDialog ofd =?new?OpenFileDialog(); ????????????ofd.Title =?"選擇要傳的文件"; ????????????ofd.InitialDirectory =?@"C:\Users\Administrator\Desktop"; ????????????ofd.Filter =?"文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ????????????ofd.ShowDialog(); ????????????//得到選擇文件的路徑 ????????????txtPath.Text = ofd.FileName; ????????} |
然后在發送文件
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | /// <summary> ????????/// 發送文件 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSendFile_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請選擇要發送的客戶端"); ????????????????return; ????????????} ????????????string?filePath = txtPath.Text; ????????????if?(string.IsNullOrEmpty(filePath)) ????????????{ ????????????????MessageBox.Show("請選擇文件"); ????????????????return; ????????????} ????????????Thread td =?new?Thread(SendBigFile); ????????????td.IsBackground =?true; ????????????td.Start(); ????????????? ????????} ? ????????/// <summary> ????????/// 大文件斷點傳送 ????????/// </summary> ????????private?void?SendBigFile() ????????{ ????????????string?filePath = txtPath.Text; ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????try ????????????{ ????????????????//讀取選擇的文件 ????????????????using?(FileStream fsRead =?new?FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)) ????????????????{ ????????????????????//1. 第一步:發送一個包,表示文件的長度,讓客戶端知道后續要接收幾個包來重新組織成一個文件 ????????????????????long?length = fsRead.Length; ????????????????????byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString()); ????????????????????//獲得發送的信息時候,在數組前面加上一個字節 1 ????????????????????List<byte> list =?new?List<byte>(); ????????????????????list.Add(1); ????????????????????list.AddRange(byteLength); ????????????????????socketSend.Send(list.ToArray());?// ????????????????????//2. 第二步:每次發送一個4KB的包,如果文件較大,則會拆分為多個包 ????????????????????byte[] buffer =?new?byte[1024 * 1024]; ????????????????????long?send = 0;?//發送的字節數?????????????????? ????????????????????while?(true)??//大文件斷點多次傳輸 ????????????????????{ ????????????????????????int?r = fsRead.Read(buffer, 0, buffer.Length); ????????????????????????if?(r == 0) ????????????????????????{ ????????????????????????????break; ????????????????????????} ????????????????????????socketSend.Send(buffer, 0, r, SocketFlags.None); ????????????????????????send += r; ????????????????????????ShowMsg(string.Format("{0}: 已發送:{1}/{2}", socketSend.RemoteEndPoint, send, length)); ????????????????????} ????????????????????ShowMsg("發送完成"); ????????????????????txtPath.Text =?""; ????????????????} ????????????} ????????????catch ????????????{ ? ????????????} ????????} |
注意:(1)發送文件的時候會分兩步發送 :第一步:發送一個包,表示文件的長度,讓客戶端知道后續要接收幾個包來重新組織成一個文件
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???第二步:每次發送一個1MB的包,如果文件較大,則會拆分為多個包
? ? ?(2)每個客戶端連接服務端的啥時候,都會把客戶端的ip以及端口號,放到下拉框里,想給那個客戶端發信息,就選擇對應的客戶端
二、客戶端的建立
1.客戶端的項目建立以及頁面布局
客戶端的界面布局與服務端很像,就是把對應的開始監聽換成連接,當然代碼也會有所改變,后面會講到·····
2.各功能按鍵的事件代碼
先把整個服客戶端的代碼貼出來,然后我們在一一講解
namespace SocketClient {public partial class Form1 : Form{//說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息// 0:表示傳遞的是字符串信息// 1:表示傳遞的是文件信息// 2:表示的是震動/// <summary>/// 用來存放連接服務的IP地址和端口號,對應的Socket (這個為了以后的擴展用,現在暫時沒用)/// </summary>Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();/// <summary>/// 存儲保存文件的路徑/// </summary>string filePath = "";/// <summary>/// 負責通信的Socket/// </summary>Socket socketSend; public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){//不檢測跨線程之間的空間調用Control.CheckForIllegalCrossThreadCalls = false;}/// <summary>/// 建立連接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnStart_Click(object sender, EventArgs e){try{//創建負責通信的SocketsocketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//獲取服務端的IPIPAddress ip = IPAddress.Parse(txtServer.Text.Trim());//獲取服務端的端口號IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));//獲得要連接的遠程服務器應用程序的IP地址和端口號socketSend.Connect(port);ShowMsg("連接成功");//新建線程,去接收客戶端發來的信息Thread td = new Thread(AcceptMgs);td.IsBackground = true;td.Start();}catch { }}/// <summary>/// 接收數據/// </summary>private void AcceptMgs(){ try{/// <summary>/// 存儲大文件的大小/// </summary>long length = 0;long recive = 0; //接收的大文件總的字節數while (true){byte[] buffer = new byte[1024 * 1024];int r = socketSend.Receive(buffer);if (r == 0){break;}if (length > 0) //判斷大文件是否已經保存完{//保存接收的文件using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write)){fsWrite.Write(buffer, 0, r);length -= r; //減去每次保存的字節數ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));if (length <= 0){ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");}continue;} }if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息{string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);}else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件{length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));recive = length;filePath = "";SaveFileDialog sfd = new SaveFileDialog();sfd.Title = "保存文件";sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";//如果沒有選擇保存文件路徑就一直打開保存框while (true){sfd.ShowDialog(this);filePath = sfd.FileName;if (string.IsNullOrEmpty(filePath)){continue;}else{break;}} }else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動{ZD();}}}catch { }}/// <summary>/// 顯示信息/// </summary>/// <param name="message"></param>private void ShowMsg(string message){txtLog.AppendText(message + "\r\n");}/// <summary>/// 發送數據/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSend_Click(object sender, EventArgs e){try{byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);//獲得發送的信息時候,在數組前面加上一個字節 0List<byte> list = new List<byte>();list.Add(0);list.AddRange(buffer);//將泛型集合轉換為數組byte[] newBuffer = list.ToArray();//將了標識字符的字節數組傳遞給客戶端socketSend.Send(newBuffer);txtMsg.Text = "";}catch{} } /// <summary>/// 選擇文件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSelect_Click(object sender, EventArgs e){//打開文件OpenFileDialog ofd = new OpenFileDialog();ofd.Title = "選擇要傳的文件";ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";ofd.ShowDialog();//得到選擇文件的路徑txtPath.Text = ofd.FileName;}/// <summary>/// 發送文件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSendFile_Click(object sender, EventArgs e){ try{string filePath = txtPath.Text;if (string.IsNullOrEmpty(filePath)){MessageBox.Show("請選擇文件");return;}//讀取選擇的文件using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)){byte[] buffer = new byte[1024 * 1024 * 2];int r = fsRead.Read(buffer, 0, buffer.Length);//獲得發送的信息時候,在數組前面加上一個字節 1List<byte> list = new List<byte>();list.Add(1);list.AddRange(buffer);byte[] newBuffer = list.ToArray();//將了標識字符的字節數組傳遞給客戶端socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);txtPath.Text = "";}}catch{ }}/// <summary>/// 震動/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnZD_Click(object sender, EventArgs e){ try{// 首字節是2說明是震動byte[] buffer = new byte[1];buffer[0] = 2;socketSend.Send(buffer);}catch{ }}/// <summary>/// 震動/// </summary>private void ZD(){//獲取當前窗體的坐標Point point = this.Location;//反復給窗體坐標復制一百次,達到震動的效果for (int i = 0; i < 100; i++){this.Location = new Point(point.X - 5, point.Y - 5);this.Location = new Point(point.X + 5, point.Y + 5);}this.Location = point;}} }1)傳輸類型說明以及全局變量
?
這些說明以及全局變量,說的也比較清楚,也不累贅了。
2)Socket通信服務端具體步驟:
(這些步驟都是根據第一個圖來的)
?(1)建立一個通信的Socket
/// <summary>/// 建立連接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnStart_Click(object sender, EventArgs e){try{//創建負責通信的SocketsocketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//獲取服務端的IPIPAddress ip = IPAddress.Parse(txtServer.Text.Trim());//獲取服務端的端口號IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));//獲得要連接的遠程服務器應用程序的IP地址和端口號socketSend.Connect(port);ShowMsg("連接成功");//新建線程,去接收客戶端發來的信息Thread td = new Thread(AcceptMgs);td.IsBackground = true;td.Start();}catch { }}在連接按鈕里,我們建立了Socket
由于客戶端會不停的去監視接收服務端發來的信息,如果把這個工作放到主線程里,程序會出現假死的現象,所以這里給他放到一個新的線程里。
(2)接收信息
/// <summary>/// 接收數據/// </summary>private void AcceptMgs(){ try{/// <summary>/// 存儲大文件的大小/// </summary>long length = 0;long recive = 0; //接收的大文件總的字節數while (true){byte[] buffer = new byte[1024 * 1024];int r = socketSend.Receive(buffer);if (r == 0){break;}if (length > 0) //判斷大文件是否已經保存完{//保存接收的文件using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write)){fsWrite.Write(buffer, 0, r);length -= r; //減去每次保存的字節數ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));if (length <= 0){ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");}continue;} }if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息{string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);}else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件{length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));recive = length;filePath = "";SaveFileDialog sfd = new SaveFileDialog();sfd.Title = "保存文件";sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";//如果沒有選擇保存文件路徑就一直打開保存框while (true){sfd.ShowDialog(this);filePath = sfd.FileName;if (string.IsNullOrEmpty(filePath)){continue;}else{break;}} }else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動{ZD();}}}catch { }}接收信息是會根據接收到字節數字的第一個字節來判斷接收到的是什么,如果接收的是個大文件,首先會接收大文件的大小,然后根據大小接收相同大小的字節數組追加保存到一個文件里去。
(3)發送數據(這里分發送字符串、文件(包含大文件)、震動)
發送字符串信息
/// <summary>/// 發送數據/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSend_Click(object sender, EventArgs e){try{byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);//獲得發送的信息時候,在數組前面加上一個字節 0List<byte> list = new List<byte>();list.Add(0);list.AddRange(buffer);//將泛型集合轉換為數組byte[] newBuffer = list.ToArray();//將了標識字符的字節數組傳遞給客戶端socketSend.Send(newBuffer);txtMsg.Text = "";}catch{} }發送震動
/// <summary>/// 震動/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnZD_Click(object sender, EventArgs e){ try{// 首字節是2說明是震動byte[] buffer = new byte[1];buffer[0] = 2;socketSend.Send(buffer);}catch{ }}/// <summary>/// 震動/// </summary>private void ZD(){//獲取當前窗體的坐標Point point = this.Location;//反復給窗體坐標復制一百次,達到震動的效果for (int i = 0; i < 100; i++){this.Location = new Point(point.X - 5, point.Y - 5);this.Location = new Point(point.X + 5, point.Y + 5);}this.Location = point;}發送文件(不包含大文件)
首先要選擇文件
/// <summary>/// 選擇文件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSelect_Click(object sender, EventArgs e){//打開文件OpenFileDialog ofd = new OpenFileDialog();ofd.Title = "選擇要傳的文件";ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";ofd.ShowDialog();//得到選擇文件的路徑txtPath.Text = ofd.FileName;}然后在發送文件
/// <summary>/// 發送文件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSendFile_Click(object sender, EventArgs e){ try{string filePath = txtPath.Text;if (string.IsNullOrEmpty(filePath)){MessageBox.Show("請選擇文件");return;}//讀取選擇的文件using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)){byte[] buffer = new byte[1024 * 1024 * 2];int r = fsRead.Read(buffer, 0, buffer.Length);//獲得發送的信息時候,在數組前面加上一個字節 1List<byte> list = new List<byte>();list.Add(1);list.AddRange(buffer);byte[] newBuffer = list.ToArray();//將了標識字符的字節數組傳遞給客戶端socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);txtPath.Text = "";}}catch{ }}?
總結
以上是生活随笔為你收集整理的C# Socket服务端与客户端通信(包含大文件的断点传输)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决:无法获取实体类com.xxx.xx
- 下一篇: C# Socket案例(服务端与客户端)