C# Socket服务端与客户端通信(包含大文件的断点传输)
步驟:
一、服務(wù)端的建立
1.服務(wù)端的項(xiàng)目建立以及頁面布局
2.各功能按鍵的事件代碼
1)傳輸類型說明以及全局變量
2)Socket通信服務(wù)端具體步驟:
? (1)建立一個(gè)Socket
? (2)接收信息
? (3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng))
二、客戶端的建立
1.服務(wù)端的項(xiàng)目建立以及頁面布局
2.各功能按鍵的事件代碼
1)傳輸類型說明以及全局變量
2)Socket通信服務(wù)端具體步驟:
? (1)建立一個(gè)Socket
? (2)接收信息
? (3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng))
?
?
注意:此圖是Socket通信的精華,在使用Socket通信時(shí),有什么迷惑的可以看看此圖,下面我們講解的時(shí)候也是參照此圖
?
Socket大家肯定很熟悉,對(duì)已內(nèi)部的通信邏輯,肯定也有一定得了解---
對(duì)于Socket研究了兩天寫了一個(gè)小程序,通過Socket服務(wù)端與客戶端的通信,以及大文件之間斷點(diǎn)的傳輸(這里只做了服務(wù)端給客戶端傳送大文件,如果想把客戶端的大文件傳送給服務(wù)端也是一樣的道理,看了文章,大家肯定可以自己實(shí)現(xiàn))······
(自己才疏學(xué)淺,如有bug請(qǐng)諒解,但功能還是能實(shí)現(xiàn)的)
下面根據(jù)步驟進(jìn)入正題:
一、服務(wù)端的建立
1.服務(wù)端的項(xiàng)目建立以及頁面布局
新建解決方案“Socket通信”以及兩個(gè)Winform項(xiàng)目(1)SockeClient——客戶端 ? ?(2)SocketServer——服務(wù)器
給服務(wù)端界面布局——參照上圖(這個(gè)大家肯定都是手到擒來就不累贅了······)
2.各功能按鍵的事件代碼
先把整個(gè)服務(wù)端的代碼貼出來,然后我們?cè)谝灰恢v解
| 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 ????{ ? ????????//說明:在傳遞信息的時(shí)候,會(huì)在需要傳遞的信息前面加一個(gè)字符來標(biāo)識(shí)傳遞的是不同的信息 ????????// 0:表示傳遞的是字符串信息 ????????// 1:表示傳遞的是文件信息 ????????// 2:表示的是震動(dòng) ? ????????/// <summary> ????????/// 用來存放連接服務(wù)的客戶端的IP地址和端口號(hào),對(duì)應(yīng)的Socket ????????/// </summary> ????????Dictionary<string, Socket> dicSocket =?new?Dictionary<string, Socket>(); ? ????????public?Form1() ????????{ ????????????InitializeComponent(); ????????} ? ????????private?void?Form1_Load(object?sender, EventArgs e) ????????{ ????????????//不檢測跨線程之間的空間調(diào)用 ????????????Control.CheckForIllegalCrossThreadCalls =?false; ????????} ? ????????/// <summary> ????????/// 開啟監(jiān)聽 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnStart_Click(object?sender, EventArgs e) ????????{ ????????????try ????????????{ ????????????????//當(dāng)點(diǎn)擊開始監(jiān)聽的時(shí)候 在服務(wù)器端創(chuàng)建一個(gè)負(fù)責(zé)監(jiān)IP地址跟端口號(hào)的Socket ????????????????Socket socketWatch =?new?Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ????????????????//獲取IP ????????????????IPAddress ip = IPAddress.Any; ????????????????//創(chuàng)建端口號(hào) ????????????????IPEndPoint port =?new?IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); ????????????????//監(jiān)聽 ????????????????socketWatch.Bind(port); ????????????????ShowMsg("監(jiān)聽成功"); ????????????????socketWatch.Listen(10); ????????????????//新建線程,去接收客戶端發(fā)來的信息 ????????????????Thread td =?new?Thread(AcceptMgs); ????????????????td.IsBackground =?true; ????????????????td.Start(socketWatch); ????????????} ????????????catch ????????????{ ????????????????? ????????????}?????????? ????????} ? ????????/// <summary> ????????/// 接收客戶端發(fā)送的信息 ????????/// </summary> ????????/// <param name="o"></param> ????????private?void?AcceptMgs(object?o) ????????{ ????????????try ????????????{ ????????????????Socket socketWatc = (Socket)o; ????????????????while?(true) ????????????????{ ????????????????????負(fù)責(zé)跟客戶端通信的Socket ????????????????????Socket socketSend = socketWatc.Accept(); ????????????????????//將遠(yuǎn)程連接的客戶端的IP地址和Socket存入集合中 ????????????????????dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend); ????????????????????//將遠(yuǎn)程連接的客戶端的IP地址和端口號(hào)存儲(chǔ)下拉框中 ????????????????????cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString()); ????????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": 連接成功"); ????????????????????//新建線程循環(huán)接收客戶端發(fā)來的信息 ????????????????????Thread td =?new?Thread(Recive); ????????????????????td.IsBackground =?true; ????????????????????td.Start(socketSend); ????????????????} ????????????} ????????????catch?{ } ????????????? ????????} ? ????????/// <summary> ????????/// 接收客戶端發(fā)來的數(shù)據(jù),并顯示出來 ????????/// </summary> ????????private?void?Recive(object?o) ????????{ ????????????Socket socketSend = (Socket)o; ????????????try ????????????{ ????????????????while?(true) ????????????????{ ????????????????????//客戶端連接成功后,服務(wù)器應(yīng)該接受客戶端發(fā)來的消息 ????????????????????? ????????????????????if?(socketSend ==?null) ????????????????????{ ????????????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????????????continue; ????????????????????} ????????????????????byte[] buffer =?new?byte[1024 * 1024 * 2]; ????????????????????//實(shí)際接受到的有效字節(jié)數(shù) ????????????????????int?r = socketSend.Receive(buffer); ????????????????????//如果客戶端關(guān)閉,發(fā)送的數(shù)據(jù)就為空,然后就跳出循環(huán) ????????????????????if?(r == 0) ????????????????????{ ????????????????????????break; ????????????????????}?????????????????? ????????????????????if?(buffer[0] == 0)?//如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說明接收的字符串信息 ????????????????????{ ????????????????????????string?strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ????????????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": "?+ strMsg); ????????????????????} ????????????????????else?if?(buffer[0] == 1)?//如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是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)?//如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說明接收的是震動(dòng) ????????????????????{ ????????????????????????ZD(); ????????????????????} ????????????????} ????????????} ????????????catch{}????????? ????????} ? ? ????????/// <summary> ????????/// 顯示信息 ????????/// </summary> ????????/// <param name="message"></param> ????????private?void?ShowMsg(string?message) ????????{ ????????????txtLog.AppendText(message +?"\r\n"); ????????} ? ????????/// <summary> ????????/// 發(fā)送信息 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSend_Click(object?sender, EventArgs e) ????????{ ????????????? ????????????//獲得選中客戶端ip對(duì)應(yīng)的通信Socket?????? ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????string?strSend=txtMsg.Text; ????????????try ????????????{ ????????????????byte[] buffer = Encoding.UTF8.GetBytes(strSend); ????????????????//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0 ????????????????List<byte> list =?new?List<byte>(); ????????????????list.Add(0); ????????????????list.AddRange(buffer); ????????????????//將泛型集合轉(zhuǎn)換為數(shù)組 ????????????????byte[] newBuffer = list.ToArray(); ????????????????//將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 ????????????????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> ????????/// 發(fā)送文件 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSendFile_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發(fā)送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????string?filePath = txtPath.Text; ????????????if?(string.IsNullOrEmpty(filePath)) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇文件"); ????????????????return; ????????????} ????????????Thread td =?new?Thread(SendBigFile); ????????????td.IsBackground =?true; ????????????td.Start(); ????????????? ????????} ? ????????/// <summary> ????????/// 大文件斷點(diǎn)傳送 ????????/// </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. 第一步:發(fā)送一個(gè)包,表示文件的長度,讓客戶端知道后續(xù)要接收幾個(gè)包來重新組織成一個(gè)文件 ????????????????????long?length = fsRead.Length; ????????????????????byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString()); ????????????????????//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1 ????????????????????List<byte> list =?new?List<byte>(); ????????????????????list.Add(1); ????????????????????list.AddRange(byteLength); ????????????????????socketSend.Send(list.ToArray());?// ????????????????????//2. 第二步:每次發(fā)送一個(gè)1MB的包,如果文件較大,則會(huì)拆分為多個(gè)包 ????????????????????byte[] buffer =?new?byte[1024 * 1024]; ????????????????????long?send = 0;?//發(fā)送的字節(jié)數(shù)?????????????????? ????????????????????while?(true)??//大文件斷點(diǎn)多次傳輸 ????????????????????{ ????????????????????????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}: 已發(fā)送:{1}/{2}", socketSend.RemoteEndPoint, send, length)); ????????????????????} ????????????????????ShowMsg("發(fā)送完成"); ????????????????????txtPath.Text =?""; ????????????????} ????????????} ????????????catch ????????????{ ? ????????????} ????????} ? ????????/// <summary> ????????/// 震動(dòng) ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnZD_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發(fā)送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????try ????????????{ ????????????????// 首字節(jié)是2說明是震動(dòng) ????????????????byte[] buffer =?new?byte[1]; ????????????????buffer[0] = 2; ????????????????socketSend.Send(buffer); ????????????} ????????????catch ????????????{ ????????????????? ????????????} ????????????? ????????} ? ????????/// <summary> ????????/// 震動(dòng) ????????/// </summary> ????????private?void?ZD() ????????{ ????????????//獲取當(dāng)前窗體的坐標(biāo) ????????????Point point =?this.Location; ????????????//反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果 ????????????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通信服務(wù)端具體步驟:
(這些步驟都是根據(jù)第一個(gè)圖來的)
?(1)建立一個(gè)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> ????????/// 開啟監(jiān)聽 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnStart_Click(object?sender, EventArgs e) ????????{ ????????????try ????????????{ ????????????????//當(dāng)點(diǎn)擊開始監(jiān)聽的時(shí)候 在服務(wù)器端創(chuàng)建一個(gè)負(fù)責(zé)監(jiān)IP地址跟端口號(hào)的Socket ????????????????Socket socketWatch =?new?Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ????????????????//獲取IP ????????????????IPAddress ip = IPAddress.Any; ????????????????//創(chuàng)建端口號(hào) ????????????????IPEndPoint port =?new?IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); ????????????????//監(jiān)聽 ????????????????socketWatch.Bind(port); ????????????????ShowMsg("監(jiān)聽成功"); ????????????????socketWatch.Listen(10); ????????????????//新建線程,去接收客戶端發(fā)來的信息 ????????????????Thread td =?new?Thread(AcceptMgs); ????????????????td.IsBackground =?true; ????????????????td.Start(socketWatch); ????????????} ????????????catch ????????????{ ????????????????? ????????????}?????????? ????????} |
在開啟監(jiān)聽按鈕里,我們建立了Socket,以及監(jiān)聽的最大客戶端數(shù)?socketWatch.Listen(10)
由于服務(wù)端會(huì)不停的去監(jiān)視接收客戶端發(fā)來的信息,如果把這個(gè)工作放到主線程里,程序會(huì)出現(xiàn)假死的現(xiàn)象,所以這里給他放到一個(gè)新的線程里。
(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> ????????/// 接收客戶端發(fā)送的信息 ????????/// </summary> ????????/// <param name="o"></param> ????????private?void?AcceptMgs(object?o) ????????{ ????????????try ????????????{ ????????????????Socket socketWatc = (Socket)o; ????????????????while?(true) ????????????????{ ????????????????????負(fù)責(zé)跟客戶端通信的Socket ????????????????????Socket socketSend = socketWatc.Accept(); ????????????????????//將遠(yuǎn)程連接的客戶端的IP地址和Socket存入集合中 ????????????????????dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend); ????????????????????//將遠(yuǎn)程連接的客戶端的IP地址和端口號(hào)存儲(chǔ)下拉框中 ????????????????????cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString()); ????????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": 連接成功"); ????????????????????//新建線程循環(huán)接收客戶端發(fā)來的信息 ????????????????????Thread td =?new?Thread(Recive); ????????????????????td.IsBackground =?true; ????????????????????td.Start(socketSend); ????????????????} ????????????} ????????????catch?{ } ????????????? ????????} |
接收信息是會(huì)根據(jù)接收到字節(jié)數(shù)字的第一個(gè)字節(jié)來判斷接收到的是什么
這個(gè)在方法Recive里進(jìn)行判斷
| 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 | /// 接收客戶端發(fā)來的數(shù)據(jù),并顯示出來 /// </summary> private?void?Recive(object?o) { ????Socket socketSend = (Socket)o; ????try ????{ ????????while?(true) ????????{ ????????????//客戶端連接成功后,服務(wù)器應(yīng)該接受客戶端發(fā)來的消息 ????????????? ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????continue; ????????????} ????????????byte[] buffer =?new?byte[1024 * 1024 * 2]; ????????????//實(shí)際接受到的有效字節(jié)數(shù) ????????????int?r = socketSend.Receive(buffer); ????????????//如果客戶端關(guān)閉,發(fā)送的數(shù)據(jù)就為空,然后就跳出循環(huán) ????????????if?(r == 0) ????????????{ ????????????????break; ????????????}?????????????????? ????????????if?(buffer[0] == 0)?//如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說明接收的字符串信息 ????????????{ ????????????????string?strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ????????????????ShowMsg(socketSend.RemoteEndPoint.ToString() +?": "?+ strMsg); ????????????} ????????????else?if?(buffer[0] == 1)?//如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是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)?//如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說明接收的是震動(dòng) ????????????{ ????????????????ZD(); ????????????} ????????} ????} ????catch{}????????? } |
(3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng))
發(fā)送字符串信息
| 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> ????????/// 發(fā)送信息 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSend_Click(object?sender, EventArgs e) ????????{ ????????????? ????????????//獲得選中客戶端ip對(duì)應(yīng)的通信Socket?????? ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????string?strSend=txtMsg.Text; ????????????try ????????????{ ????????????????byte[] buffer = Encoding.UTF8.GetBytes(strSend); ????????????????//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0 ????????????????List<byte> list =?new?List<byte>(); ????????????????list.Add(0); ????????????????list.AddRange(buffer); ????????????????//將泛型集合轉(zhuǎn)換為數(shù)組 ????????????????byte[] newBuffer = list.ToArray(); ????????????????//將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 ????????????????socketSend.Send(newBuffer); ????????????????txtMsg.Text =?""; ????????????} ????????????catch ????????????{ ????????????}??????????? ????????} |
發(fā)送震動(dòng)
| 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> ????????/// 震動(dòng) ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnZD_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發(fā)送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????try ????????????{ ????????????????// 首字節(jié)是2說明是震動(dòng) ????????????????byte[] buffer =?new?byte[1]; ????????????????buffer[0] = 2; ????????????????socketSend.Send(buffer); ????????????} ????????????catch ????????????{ ????????????????? ????????????} ????????????? ????????} ? ????????/// <summary> ????????/// 震動(dòng) ????????/// </summary> ????????private?void?ZD() ????????{ ????????????//獲取當(dāng)前窗體的坐標(biāo) ????????????Point point =?this.Location; ????????????//反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果 ????????????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; ????????} |
發(fā)送文件(包含大文件)
首先要選擇文件
| 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; ????????} |
然后在發(fā)送文件
| 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> ????????/// 發(fā)送文件 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?void?btnSendFile_Click(object?sender, EventArgs e) ????????{ ????????????//判斷是否選擇了要發(fā)送的客戶端 ????????????if?(cboUsers.SelectedItem ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; ????????????if?(socketSend ==?null) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); ????????????????return; ????????????} ????????????string?filePath = txtPath.Text; ????????????if?(string.IsNullOrEmpty(filePath)) ????????????{ ????????????????MessageBox.Show("請(qǐng)選擇文件"); ????????????????return; ????????????} ????????????Thread td =?new?Thread(SendBigFile); ????????????td.IsBackground =?true; ????????????td.Start(); ????????????? ????????} ? ????????/// <summary> ????????/// 大文件斷點(diǎn)傳送 ????????/// </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. 第一步:發(fā)送一個(gè)包,表示文件的長度,讓客戶端知道后續(xù)要接收幾個(gè)包來重新組織成一個(gè)文件 ????????????????????long?length = fsRead.Length; ????????????????????byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString()); ????????????????????//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1 ????????????????????List<byte> list =?new?List<byte>(); ????????????????????list.Add(1); ????????????????????list.AddRange(byteLength); ????????????????????socketSend.Send(list.ToArray());?// ????????????????????//2. 第二步:每次發(fā)送一個(gè)4KB的包,如果文件較大,則會(huì)拆分為多個(gè)包 ????????????????????byte[] buffer =?new?byte[1024 * 1024]; ????????????????????long?send = 0;?//發(fā)送的字節(jié)數(shù)?????????????????? ????????????????????while?(true)??//大文件斷點(diǎn)多次傳輸 ????????????????????{ ????????????????????????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}: 已發(fā)送:{1}/{2}", socketSend.RemoteEndPoint, send, length)); ????????????????????} ????????????????????ShowMsg("發(fā)送完成"); ????????????????????txtPath.Text =?""; ????????????????} ????????????} ????????????catch ????????????{ ? ????????????} ????????} |
注意:(1)發(fā)送文件的時(shí)候會(huì)分兩步發(fā)送 :第一步:發(fā)送一個(gè)包,表示文件的長度,讓客戶端知道后續(xù)要接收幾個(gè)包來重新組織成一個(gè)文件
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???第二步:每次發(fā)送一個(gè)1MB的包,如果文件較大,則會(huì)拆分為多個(gè)包
? ? ?(2)每個(gè)客戶端連接服務(wù)端的啥時(shí)候,都會(huì)把客戶端的ip以及端口號(hào),放到下拉框里,想給那個(gè)客戶端發(fā)信息,就選擇對(duì)應(yīng)的客戶端
二、客戶端的建立
1.客戶端的項(xiàng)目建立以及頁面布局
客戶端的界面布局與服務(wù)端很像,就是把對(duì)應(yīng)的開始監(jiān)聽換成連接,當(dāng)然代碼也會(huì)有所改變,后面會(huì)講到·····
2.各功能按鍵的事件代碼
先把整個(gè)服客戶端的代碼貼出來,然后我們?cè)谝灰恢v解
namespace SocketClient {public partial class Form1 : Form{//說明:在傳遞信息的時(shí)候,會(huì)在需要傳遞的信息前面加一個(gè)字符來標(biāo)識(shí)傳遞的是不同的信息// 0:表示傳遞的是字符串信息// 1:表示傳遞的是文件信息// 2:表示的是震動(dòng)/// <summary>/// 用來存放連接服務(wù)的IP地址和端口號(hào),對(duì)應(yīng)的Socket (這個(gè)為了以后的擴(kuò)展用,現(xiàn)在暫時(shí)沒用)/// </summary>Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();/// <summary>/// 存儲(chǔ)保存文件的路徑/// </summary>string filePath = "";/// <summary>/// 負(fù)責(zé)通信的Socket/// </summary>Socket socketSend; public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){//不檢測跨線程之間的空間調(diào)用Control.CheckForIllegalCrossThreadCalls = false;}/// <summary>/// 建立連接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnStart_Click(object sender, EventArgs e){try{//創(chuàng)建負(fù)責(zé)通信的SocketsocketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//獲取服務(wù)端的IPIPAddress ip = IPAddress.Parse(txtServer.Text.Trim());//獲取服務(wù)端的端口號(hào)IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));//獲得要連接的遠(yuǎn)程服務(wù)器應(yīng)用程序的IP地址和端口號(hào)socketSend.Connect(port);ShowMsg("連接成功");//新建線程,去接收客戶端發(fā)來的信息Thread td = new Thread(AcceptMgs);td.IsBackground = true;td.Start();}catch { }}/// <summary>/// 接收數(shù)據(jù)/// </summary>private void AcceptMgs(){ try{/// <summary>/// 存儲(chǔ)大文件的大小/// </summary>long length = 0;long recive = 0; //接收的大文件總的字節(jié)數(shù)while (true){byte[] buffer = new byte[1024 * 1024];int r = socketSend.Receive(buffer);if (r == 0){break;}if (length > 0) //判斷大文件是否已經(jīng)保存完{//保存接收的文件using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write)){fsWrite.Write(buffer, 0, r);length -= r; //減去每次保存的字節(jié)數(shù)ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));if (length <= 0){ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");}continue;} }if (buffer[0] == 0) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說明接收的字符串信息{string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);}else if (buffer[0] == 1) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是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) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說明接收的是震動(dòng){ZD();}}}catch { }}/// <summary>/// 顯示信息/// </summary>/// <param name="message"></param>private void ShowMsg(string message){txtLog.AppendText(message + "\r\n");}/// <summary>/// 發(fā)送數(shù)據(jù)/// </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);//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0List<byte> list = new List<byte>();list.Add(0);list.AddRange(buffer);//將泛型集合轉(zhuǎn)換為數(shù)組byte[] newBuffer = list.ToArray();//將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端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>/// 發(fā)送文件/// </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("請(qǐng)選擇文件");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);//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1List<byte> list = new List<byte>();list.Add(1);list.AddRange(buffer);byte[] newBuffer = list.ToArray();//將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);txtPath.Text = "";}}catch{ }}/// <summary>/// 震動(dòng)/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnZD_Click(object sender, EventArgs e){ try{// 首字節(jié)是2說明是震動(dòng)byte[] buffer = new byte[1];buffer[0] = 2;socketSend.Send(buffer);}catch{ }}/// <summary>/// 震動(dòng)/// </summary>private void ZD(){//獲取當(dāng)前窗體的坐標(biāo)Point point = this.Location;//反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果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通信服務(wù)端具體步驟:
(這些步驟都是根據(jù)第一個(gè)圖來的)
?(1)建立一個(gè)通信的Socket
/// <summary>/// 建立連接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnStart_Click(object sender, EventArgs e){try{//創(chuàng)建負(fù)責(zé)通信的SocketsocketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//獲取服務(wù)端的IPIPAddress ip = IPAddress.Parse(txtServer.Text.Trim());//獲取服務(wù)端的端口號(hào)IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));//獲得要連接的遠(yuǎn)程服務(wù)器應(yīng)用程序的IP地址和端口號(hào)socketSend.Connect(port);ShowMsg("連接成功");//新建線程,去接收客戶端發(fā)來的信息Thread td = new Thread(AcceptMgs);td.IsBackground = true;td.Start();}catch { }}在連接按鈕里,我們建立了Socket
由于客戶端會(huì)不停的去監(jiān)視接收服務(wù)端發(fā)來的信息,如果把這個(gè)工作放到主線程里,程序會(huì)出現(xiàn)假死的現(xiàn)象,所以這里給他放到一個(gè)新的線程里。
(2)接收信息
/// <summary>/// 接收數(shù)據(jù)/// </summary>private void AcceptMgs(){ try{/// <summary>/// 存儲(chǔ)大文件的大小/// </summary>long length = 0;long recive = 0; //接收的大文件總的字節(jié)數(shù)while (true){byte[] buffer = new byte[1024 * 1024];int r = socketSend.Receive(buffer);if (r == 0){break;}if (length > 0) //判斷大文件是否已經(jīng)保存完{//保存接收的文件using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write)){fsWrite.Write(buffer, 0, r);length -= r; //減去每次保存的字節(jié)數(shù)ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));if (length <= 0){ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");}continue;} }if (buffer[0] == 0) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說明接收的字符串信息{string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);}else if (buffer[0] == 1) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是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) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說明接收的是震動(dòng){ZD();}}}catch { }}接收信息是會(huì)根據(jù)接收到字節(jié)數(shù)字的第一個(gè)字節(jié)來判斷接收到的是什么,如果接收的是個(gè)大文件,首先會(huì)接收大文件的大小,然后根據(jù)大小接收相同大小的字節(jié)數(shù)組追加保存到一個(gè)文件里去。
(3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng))
發(fā)送字符串信息
/// <summary>/// 發(fā)送數(shù)據(jù)/// </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);//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0List<byte> list = new List<byte>();list.Add(0);list.AddRange(buffer);//將泛型集合轉(zhuǎn)換為數(shù)組byte[] newBuffer = list.ToArray();//將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端socketSend.Send(newBuffer);txtMsg.Text = "";}catch{} }發(fā)送震動(dòng)
/// <summary>/// 震動(dòng)/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnZD_Click(object sender, EventArgs e){ try{// 首字節(jié)是2說明是震動(dòng)byte[] buffer = new byte[1];buffer[0] = 2;socketSend.Send(buffer);}catch{ }}/// <summary>/// 震動(dòng)/// </summary>private void ZD(){//獲取當(dāng)前窗體的坐標(biāo)Point point = this.Location;//反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果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;}發(fā)送文件(不包含大文件)
首先要選擇文件
/// <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;}然后在發(fā)送文件
/// <summary>/// 發(fā)送文件/// </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("請(qǐng)選擇文件");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);//獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1List<byte> list = new List<byte>();list.Add(1);list.AddRange(buffer);byte[] newBuffer = list.ToArray();//將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);txtPath.Text = "";}}catch{ }}?
總結(jié)
以上是生活随笔為你收集整理的C# Socket服务端与客户端通信(包含大文件的断点传输)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决:无法获取实体类com.xxx.xx
- 下一篇: 解决:com.sun.jersey.ap