-
C#教程之C#之Socket操作类实例解析
本文展示了一个C#的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
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Collections; using System.Net; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.IO; using System.Collections.Specialized; using System.Threading; public class DuxSocketClient { #region 私有字段 /// <summary> /// 设置数据缓冲区大小 默认1024 /// </summary> private static int m_maxpacket = 1024 * 4; public delegate void SendFileProgress( int progress); public delegate void ReceiveFileProgress( int progress); #endregion #region 服务器侦听 /// <summary> /// 服务器侦听方法 返回null则说明没有链接上 /// </summary> /// <returns>返回一个套接字(Socket)</returns> public static Socket ListenerSocket(TcpListener listener) { try { Socket socket = listener.AcceptSocket(); return socket; } catch { return null ; } } /// <summary> /// 服务器侦听方法 返回null则说明没有链接上 /// </summary> /// <param name="listener"></param> /// <returns>返回一个网络流</returns> public static NetworkStream ListenerStream(TcpListener listener) { try { TcpClient client = listener.AcceptTcpClient(); return client.GetStream(); } catch { return null ; } } #endregion #region 客户端连接 public static Socket ConnectSocket(TcpClient tcpclient, IPEndPoint ipendpoint) { try { tcpclient.Connect(ipendpoint); return tcpclient.Client; } catch { return null ; } } public static Socket ConnectSocket(TcpClient tcpclient, IPAddress ipadd, int port) { try { tcpclient.Connect(ipadd, port); return tcpclient.Client; } catch { return null ; } } public static NetworkStream ConnectStream(TcpClient tcpclient, IPEndPoint ipendpoint) { try { tcpclient.Connect(ipendpoint); return tcpclient.GetStream(); } catch { return null ; } } public static NetworkStream ConnectStream(TcpClient tcpclient, IPAddress ipadd, int port) { try { tcpclient.Connect(ipadd, port); return tcpclient.GetStream(); } catch { return null ; } } #endregion #region Socket接收数据 /// <summary> /// 接受固定长度字符串 /// </summary> /// <param name="socket"></param> /// <param name="size"></param> /// <returns></returns> public static byte [] ReceiveFixData(Socket socket, int size) { int offset = 0; int recv = 0; int dataleft = size; byte [] msg = new byte [size]; while (dataleft > 0) { recv = socket.Receive(msg, offset, dataleft, 0); if (recv == 0) { break ; } offset += recv; dataleft -= recv; } return msg; } /// <summary> /// 接收变长字符串 /// 为了处理粘包问题 ,每次发送数据时 包头(数据字节长度) + 正文 /// 这个发送小数据 /// 设置包头的字节为8,不能超过8位数的字节数组 /// </summary> /// <param name="socket"></param> /// <returns>byte[]数组</returns> public static byte [] ReceiveVarData(Socket socket) { //每次接受数据时,接收固定长度的包头,包头长度为8 byte [] lengthbyte = ReceiveFixData(socket, 8); //length得到字符长度 然后加工处理得到数字 int length = GetPacketLength(lengthbyte); //得到正文 return ReceiveFixData(socket, length); } /// <summary> /// 接收T类对象,反序列化 /// </summary> /// <typeparam name="T">接收T类对象,T类必须是一个可序列化类</typeparam> /// <param name="socket"></param> /// <returns></returns> public static T ReceiveVarData<T>(Socket socket) { //先接收包头长度 固定8个字节 byte [] lengthbyte = ReceiveFixData(socket, 8); //得到字节长度 int length = GetPacketLength(lengthbyte); byte [] bytecoll = new byte [m_maxpacket]; IFormatter format = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); int offset = 0; //接收字节个数 int lastdata = length; //还剩下多少没有接收,初始大小等于实际大小 int receivedata = m_maxpacket; //每次接收大小 //循环接收 int mark = 0; //标记几次接收到的数据为0长度 while ( true ) { //剩下的字节数是否小于缓存大小 if (lastdata < m_maxpacket) { receivedata = lastdata; //就只接收剩下的字节数 } int count = socket.Receive(bytecoll,0,receivedata,0); if (count > 0) { stream.Write(bytecoll, 0, count); offset += count; lastdata -= count; mark = 0; } else { mark++; if (mark == 10) { break ; } } if (offset == length) { break ; } } stream.Seek(0, SeekOrigin.Begin); //必须要这个 或者stream.Position = 0; T t = (T)format.Deserialize(stream); stream.Close(); return t; } /// <summary> /// 在预先得到文件的文件名和大小 /// 调用此方法接收文件 /// </summary> /// <param name="socket"></param> /// <param name="path">路径必须存在</param> public static bool ReceiveFile(Socket socket, string path, string filename, long size,ReceiveFileProgress progress) { bool ret = false ; if (Directory.Exists(path)) { //主要是防止有重名文件 string savepath = GetPath(path, filename); //得到文件路径 //缓冲区 byte [] file = new byte [m_maxpacket]; int count = 0; //每次接收的实际长度 int receivedata = m_maxpacket; //每次要接收的长度 long offset = 0; //循环接收的总长度 long lastdata = size; //剩余多少还没接收 int mark = 0; using (FileStream fs = new FileStream(savepath, FileMode.OpenOrCreate, FileAccess.Write)) { if (size > 0) { while ( true ) { if (lastdata < receivedata) { receivedata = Convert.ToInt32(lastdata); } count = socket.Receive(file, 0, receivedata, SocketFlags.None); if (count > 0) { fs.Write(file, 0, count); offset += count; lastdata -= count; mark = 0; } else { mark++; //连续5次接收为0字节 则跳出循环 if (mark ==10) { break ; } } //接收进度 if (progress != null ) { progress(Convert.ToInt32(((Convert.ToDouble(offset) / Convert.ToDouble(size)) * 100))); } //接收完毕 if (offset == size) { ret = true ; break ; } } } fs.Close(); } } return ret; } public static bool ReceiveFile(Socket socket, string path, string filename, long size) { return ReceiveFile(socket, path, filename, size, null ); } /// <summary> /// 预先不知道文件名和文件大小 用此方法接收 /// 此方法对于的发送方法是SendFile() /// </summary> /// <param name="socket"></param> /// <param name="path">要保存的目录</param> public static void ReceiveFile(Socket socket, string path) { //得到包头信息字节数组 (文件名 + 文件大小 的字符串长度) //取前8位 byte [] info_bt = ReceiveFixData(socket, 8); //得到包头信息字符长度 int info_length = GetPacketLength(info_bt); //提取包头信息,(文件名 + 文件大小 的字符串长度) byte [] info = ReceiveFixData(socket, info_length); //得到文件信息字符串 (文件名 + 文件大小) string info_str = System.Text.Encoding.UTF8.GetString(info); string [] strs = info_str.Split( '|' ); string filename = strs[0]; //文件名 long length = Convert.ToInt64(strs[1]); //文件大小 //开始接收文件 ReceiveFile(socket, path, filename, length); } private static int GetPacketLength( byte [] length) { string str = System.Text.Encoding.UTF8.GetString(length); str = str.TrimEnd( '*' ); ; //("*", ""); int _length = 0; if ( int .TryParse(str, out _length)) { return _length; } else { return 0; } } /// <summary> /// 得到文件路径(防止有文件名重复) /// 如:aaa.txt已经在directory目录下存在,则会得到文件aaa(1).txt /// </summary> /// <param name="directory">目录名</param> /// <param name="file">文件名</param> /// <returns>文件路径</returns> static int i = 0; static string markPath = String.Empty; public static string GetPath( string directory, string file) { if (markPath == String.Empty) { markPath = Path.Combine(directory, file); } string path = Path.Combine(directory, file); if (File.Exists(path)) { i++; string filename = Path.GetFileNameWithoutExtension(markPath) + "(" + i.ToString() + ")" ; string extension = Path.GetExtension(markPath); return GetPath(directory, filename + extension); } else { i = 0; markPath = String.Empty; return path; } } #endregion #region Socket发送数据 /// <summary> /// 发送固定长度消息 /// 发送字节数不能大于int型最大值 /// </summary> /// <param name="socket"></param> /// <param name="msg"></param> /// <returns>返回发送字节个数</returns> public static int SendFixData(Socket socket, byte [] msg) { int size = msg.Length; //要发送字节长度 int offset = 0; //已经发送长度 int dataleft = size; //剩下字符 int senddata = m_maxpacket; //每次发送大小 while ( true ) { //如过剩下的字节数 小于 每次发送字节数 if (dataleft < senddata) { senddata = dataleft; } int count = socket.Send(msg, offset, senddata, SocketFlags.None); offset += count; dataleft -= count; if (offset == size) { break ; } } return offset; } /// <summary> /// 发送变长信息 格式 包头(包头占8位) + 正文 /// </summary> /// <param name="socket"></param> /// <param name="contact">发送文本</param> /// <returns></returns> public static int SendVarData(Socket socket, string contact) { //得到字符长度 int size = System.Text.Encoding.UTF8.GetBytes(contact).Length; //包头字符 string length = GetSendPacketLengthStr(size); //包头 + 正文 byte [] sendbyte = System.Text.Encoding.UTF8.GetBytes(length + contact); //发送 return SendFixData(socket, sendbyte); } /// <summary> /// 发送变成信息 /// </summary> /// <param name="socket"></param> /// <param name="bytes"></param> /// <returns></returns> public static int SendVarData(Socket socket, byte [] bytes) { //得到包头字节 int size = bytes.Length; string length = GetSendPacketLengthStr(size); byte [] lengthbyte = System.Text.Encoding.UTF8.GetBytes(length); //发送包头 SendFixData(socket, lengthbyte); //因为不知道正文是什么编码所以没有合并 //发送正文 return SendFixData(socket, bytes); } /// <summary> /// 发送T类型对象,序列化 /// </summary> /// <typeparam name="T">T类型</typeparam> /// <param name="socket"></param> /// <param name="obj">T类型对象,必须是可序列化的</param> /// <returns></returns> public static int SendSerializeObject<T>(Socket socket, T obj) { byte [] bytes = SerializeObject(obj); return SendVarData(socket, bytes); } /// <summary> /// 发送文件 /// </summary> /// <param name="socket">socket对象</param> /// <param name="path">文件路径</param> /// <param name="issend">是否发送文件(头)信息,如果当前知道文件[大小,名称]则为false</param> /// <param name="progress"></param> /// <returns></returns> public static bool SendFile(Socket socket, string path, bool issend,SendFileProgress progress) { bool ret = false ; if (File.Exists(path)) { FileInfo fileinfo = new FileInfo(path); string filename = fileinfo.Name; long length = fileinfo.Length; //发送文件信息 if (issend) { SendVarData(socket, filename + "|" + length); } //发送文件 long offset = 0; byte [] b = new byte [m_maxpacket]; int mark = 0; using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { int senddata = b.Length; long i = length; //循环读取发送 while ( true ) { int count = fs.Read(b, 0, senddata); if (count > 0) { socket.Send(b, 0, count, SocketFlags.None); offset += count; mark = 0; } else { mark++; if (mark == 10) { break ; } } if (progress != null ) { progress(Convert.ToInt32(((Convert.ToDouble(offset) / Convert.ToDouble(length)) * 100))); } if (offset == length) { break ; } Thread.Sleep(50); //设置等待时间,以免粘包 } } } return ret; } /// <summary> /// 发送文件,不需要进度信息 /// </summary> /// <param name="socket">socket对象</param> /// <param name="path">文件路径</param> /// <param name="issend">是否发生(头)信息</param> /// <returns></returns> public static bool SendFile(Socket socket, string path, bool issend) { return SendFile(socket, path, issend, null ); } /// <summary> /// 发送文件,不需要进度信息和(头)信息 /// </summary> /// <param name="socket">socket对象</param> /// <param name="path">文件路径</param> /// <returns></returns> public static bool SendFile(Socket socket, string path) { return SendFile(socket, path, false , null ); } private static byte [] SerializeObject( object obj) { IFormatter format = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); format.Serialize(stream, obj); byte [] ret = stream.ToArray(); stream.Close(); return ret; } private static string GetSendPacketLengthStr( int size) { string length = size.ToString() + "********" ; //得到size的长度 return length.Substring(0, 8); //截取前前8位 } #endregion #region NetworkStream接收数据 //没写 #endregion #region NetworkStream发送数据 //没写 #endregion } |
用法说明:
每个接收的方法都对应着有发送方法
如:
发送方法:
1
|
SendFixData(socket, "01" ); |
接收方法:
1
|
ReceiveFixData(socket,2); //size 就为2 |
不知道发送文本长度:
1
|
string txt = ???? //不知道有多少字符 |
发送方法:
1
|
SendVarData(socket,txt); //有重载版 |
接收方法:
1
|
ReceiveVarData(socket); |
希望本文所述实例对大家C#程序设计有所帮助。
栏目列表
最新更新
python爬虫及其可视化
使用python爬取豆瓣电影短评评论内容
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比