SocketClient.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. using System;
  2. using System.IO;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Runtime.Serialization;
  6. using System.Runtime.Serialization.Formatters.Binary;
  7. using System.Text;
  8. using System.Threading;
  9. namespace Masuit.Tools.Core.Net
  10. {
  11. /// <summary>
  12. /// Socket客户端操作类
  13. /// </summary>
  14. public static class SocketClient
  15. {
  16. #region 私有字段
  17. /// <summary>
  18. /// 设置数据缓冲区大小 默认1024
  19. /// </summary>
  20. private static readonly int m_maxpacket = 1024 * 4;
  21. #endregion
  22. #region 服务器侦听
  23. /// <summary>
  24. /// 服务器侦听方法 返回null则说明没有链接上
  25. /// </summary>
  26. /// <returns>返回一个套接字(Socket)</returns>
  27. public static Socket ListenerSocket(this TcpListener listener)
  28. {
  29. try
  30. {
  31. Socket socket = listener.AcceptSocket();
  32. return socket;
  33. }
  34. catch
  35. {
  36. return null;
  37. }
  38. }
  39. /// <summary>
  40. /// 服务器侦听方法 返回null则说明没有链接上
  41. /// </summary>
  42. /// <param name="listener">TCP监听对象</param>
  43. /// <returns>返回一个网络流</returns>
  44. public static NetworkStream ListenerStream(this TcpListener listener)
  45. {
  46. try
  47. {
  48. TcpClient client = listener.AcceptTcpClient();
  49. return client.GetStream();
  50. }
  51. catch
  52. {
  53. return null;
  54. }
  55. }
  56. #endregion
  57. #region 客户端连接
  58. /// <summary>
  59. /// 从客户端连接获取socket对象
  60. /// </summary>
  61. /// <param name="tcpclient">TCP客户端</param>
  62. /// <param name="ipendpoint">客户端节点</param>
  63. /// <returns>客户端socket</returns>
  64. public static Socket ConnectSocket(this TcpClient tcpclient, IPEndPoint ipendpoint)
  65. {
  66. try
  67. {
  68. tcpclient.Connect(ipendpoint);
  69. return tcpclient.Client;
  70. }
  71. catch
  72. {
  73. return null;
  74. }
  75. }
  76. /// <summary>
  77. /// 从客户端连接获取socket对象
  78. /// </summary>
  79. /// <param name="tcpclient">TCP客户端</param>
  80. /// <param name="ipadd">IP地址</param>
  81. /// <param name="port">端口号</param>
  82. /// <returns>客户端socket</returns>
  83. public static Socket ConnectSocket(this TcpClient tcpclient, IPAddress ipadd, int port)
  84. {
  85. try
  86. {
  87. tcpclient.Connect(ipadd, port);
  88. return tcpclient.Client;
  89. }
  90. catch
  91. {
  92. return null;
  93. }
  94. }
  95. /// <summary>
  96. /// 从客户端获取网络流对象
  97. /// </summary>
  98. /// <param name="tcpclient">TCP客户端</param>
  99. /// <param name="ipendpoint">客户端节点</param>
  100. /// <returns>客户端的网络流</returns>
  101. public static NetworkStream ConnectStream(this TcpClient tcpclient, IPEndPoint ipendpoint)
  102. {
  103. try
  104. {
  105. tcpclient.Connect(ipendpoint);
  106. return tcpclient.GetStream();
  107. }
  108. catch
  109. {
  110. return null;
  111. }
  112. }
  113. /// <summary>
  114. /// 从客户端获取网络流对象
  115. /// </summary>
  116. /// <param name="tcpclient">TCP客户端</param>
  117. /// <param name="ipadd">IP地址</param>
  118. /// <param name="port">端口号</param>
  119. /// <returns>客户端网络流对象</returns>
  120. public static NetworkStream ConnectStream(this TcpClient tcpclient, IPAddress ipadd, int port)
  121. {
  122. try
  123. {
  124. tcpclient.Connect(ipadd, port);
  125. return tcpclient.GetStream();
  126. }
  127. catch
  128. {
  129. return null;
  130. }
  131. }
  132. #endregion
  133. #region Socket接收数据
  134. /// <summary>
  135. /// 接受固定长度字符串
  136. /// </summary>
  137. /// <param name="socket">socket对象</param>
  138. /// <param name="size">字符串长度</param>
  139. /// <returns>字节数据</returns>
  140. public static byte[] ReceiveFixData(this Socket socket, int size)
  141. {
  142. int offset = 0;
  143. int dataleft = size;
  144. byte[] msg = new byte[size];
  145. while (dataleft > 0)
  146. {
  147. var recv = socket.Receive(msg, offset, dataleft, 0);
  148. if (recv == 0)
  149. break;
  150. offset += recv;
  151. dataleft -= recv;
  152. }
  153. return msg;
  154. }
  155. /// <summary>
  156. /// 接收变长字符串
  157. /// 为了处理粘包问题 ,每次发送数据时 包头(数据字节长度) + 正文
  158. /// 这个发送小数据
  159. /// 设置包头的字节为8,不能超过8位数的字节数组
  160. /// </summary>
  161. /// <param name="socket">客户端socket</param>
  162. /// <returns>byte[]数组</returns>
  163. public static byte[] ReceiveVarData(this Socket socket)
  164. {
  165. //每次接受数据时,接收固定长度的包头,包头长度为8
  166. byte[] lengthbyte = ReceiveFixData(socket, 8);
  167. //length得到字符长度 然后加工处理得到数字
  168. int length = GetPacketLength(lengthbyte);
  169. //得到正文
  170. return ReceiveFixData(socket, length);
  171. }
  172. /// <summary>
  173. /// 接收T类对象,反序列化
  174. /// </summary>
  175. /// <typeparam name="T">接收T类对象,T类必须是一个可序列化类</typeparam>
  176. /// <param name="socket">客户端socket</param>
  177. /// <returns>强类型对象</returns>
  178. public static T ReceiveVarData<T>(this Socket socket)
  179. {
  180. //先接收包头长度 固定8个字节
  181. byte[] lengthbyte = ReceiveFixData(socket, 8);
  182. //得到字节长度
  183. int length = GetPacketLength(lengthbyte);
  184. byte[] bytecoll = new byte[m_maxpacket];
  185. IFormatter format = new BinaryFormatter();
  186. MemoryStream stream = new MemoryStream();
  187. int offset = 0; //接收字节个数
  188. int lastdata = length; //还剩下多少没有接收,初始大小等于实际大小
  189. int receivedata = m_maxpacket; //每次接收大小
  190. //循环接收
  191. int mark = 0; //标记几次接收到的数据为0长度
  192. while (true)
  193. {
  194. //剩下的字节数是否小于缓存大小
  195. if (lastdata < m_maxpacket)
  196. receivedata = lastdata; //就只接收剩下的字节数
  197. int count = socket.Receive(bytecoll, 0, receivedata, 0);
  198. if (count > 0)
  199. {
  200. stream.Write(bytecoll, 0, count);
  201. offset += count;
  202. lastdata -= count;
  203. mark = 0;
  204. }
  205. else
  206. {
  207. mark++;
  208. if (mark == 10)
  209. break;
  210. }
  211. if (offset == length)
  212. break;
  213. }
  214. stream.Seek(0, SeekOrigin.Begin); //必须要这个 或者stream.Position = 0;
  215. T t = (T)format.Deserialize(stream);
  216. stream.Close();
  217. return t;
  218. }
  219. /// <summary>
  220. /// 在预先得到文件的文件名和大小
  221. /// 调用此方法接收文件
  222. /// </summary>
  223. /// <param name="socket">socket服务端</param>
  224. /// <param name="path">路径必须存在</param>
  225. /// <param name="filename">文件名</param>
  226. /// <param name="size">预先知道的文件大小</param>
  227. /// <param name="progress">处理过程</param>
  228. public static bool ReceiveFile(this Socket socket, string path, string filename, long size, Action<int> progress)
  229. {
  230. bool ret = false;
  231. if (Directory.Exists(path))
  232. {
  233. //主要是防止有重名文件
  234. string savepath = GetPath(path, filename); //得到文件路径
  235. //缓冲区
  236. byte[] file = new byte[m_maxpacket];
  237. int count = 0; //每次接收的实际长度
  238. int receivedata = m_maxpacket; //每次要接收的长度
  239. long offset = 0; //循环接收的总长度
  240. long lastdata = size; //剩余多少还没接收
  241. int mark = 0;
  242. using (FileStream fs = new FileStream(savepath, FileMode.OpenOrCreate, FileAccess.Write))
  243. {
  244. if (size > 0)
  245. while (true)
  246. {
  247. if (lastdata < receivedata)
  248. receivedata = Convert.ToInt32(lastdata);
  249. count = socket.Receive(file, 0, receivedata, SocketFlags.None);
  250. if (count > 0)
  251. {
  252. fs.Write(file, 0, count);
  253. offset += count;
  254. lastdata -= count;
  255. mark = 0;
  256. }
  257. else
  258. {
  259. mark++; //连续5次接收为0字节 则跳出循环
  260. if (mark == 10)
  261. break;
  262. }
  263. //接收进度
  264. if (progress != null)
  265. progress(Convert.ToInt32(Convert.ToDouble(offset) / Convert.ToDouble(size) * 100));
  266. //接收完毕
  267. if (offset == size)
  268. {
  269. ret = true;
  270. break;
  271. }
  272. }
  273. fs.Close();
  274. }
  275. }
  276. return ret;
  277. }
  278. /// <summary>
  279. /// 从socket服务端接收文件
  280. /// </summary>
  281. /// <param name="socket">socket服务端</param>
  282. /// <param name="path">文件保存路径(必须存在)</param>
  283. /// <param name="filename">文件名</param>
  284. /// <param name="size">预先知道的文件大小</param>
  285. /// <returns>处理结果</returns>
  286. public static bool ReceiveFile(this Socket socket, string path, string filename, long size)
  287. {
  288. return ReceiveFile(socket, path, filename, size, null);
  289. }
  290. /// <summary>
  291. /// 预先不知道文件名和文件大小 用此方法接收
  292. /// 此方法对于的发送方法是SendFile()
  293. /// </summary>
  294. /// <param name="socket">socket服务端</param>
  295. /// <param name="path">要保存的目录</param>
  296. public static void ReceiveFile(this Socket socket, string path)
  297. {
  298. //得到包头信息字节数组 (文件名 + 文件大小 的字符串长度)
  299. //取前8位
  300. byte[] info_bt = ReceiveFixData(socket, 8);
  301. //得到包头信息字符长度
  302. int info_length = GetPacketLength(info_bt);
  303. //提取包头信息,(文件名 + 文件大小 的字符串长度)
  304. byte[] info = ReceiveFixData(socket, info_length);
  305. //得到文件信息字符串 (文件名 + 文件大小)
  306. string info_str = Encoding.UTF8.GetString(info);
  307. string[] strs = info_str.Split('|');
  308. string filename = strs[0]; //文件名
  309. long length = Convert.ToInt64(strs[1]); //文件大小
  310. //开始接收文件
  311. ReceiveFile(socket, path, filename, length);
  312. }
  313. private static int GetPacketLength(byte[] length)
  314. {
  315. string str = Encoding.UTF8.GetString(length);
  316. str = str.TrimEnd('*');
  317. ; //("*", "");
  318. int _length = 0;
  319. if (int.TryParse(str, out _length))
  320. return _length;
  321. return 0;
  322. }
  323. private static int i;
  324. private static string markPath = string.Empty;
  325. /// <summary>
  326. /// 得到文件路径(防止有文件名重复)
  327. /// 如:aaa.txt已经在directory目录下存在,则会得到文件aaa(1).txt
  328. /// </summary>
  329. /// <param name="directory">目录名</param>
  330. /// <param name="file">文件名</param>
  331. /// <returns>文件路径</returns>
  332. public static string GetPath(string directory, string file)
  333. {
  334. if (markPath == string.Empty)
  335. markPath = Path.Combine(directory, file);
  336. string path = Path.Combine(directory, file);
  337. if (File.Exists(path))
  338. {
  339. i++;
  340. string filename = Path.GetFileNameWithoutExtension(markPath) + "(" + i + ")";
  341. string extension = Path.GetExtension(markPath);
  342. return GetPath(directory, filename + extension);
  343. }
  344. i = 0;
  345. markPath = string.Empty;
  346. return path;
  347. }
  348. #endregion
  349. #region Socket发送数据
  350. /// <summary>
  351. /// 发送固定长度消息
  352. /// 发送字节数不能大于int型最大值
  353. /// </summary>
  354. /// <param name="socket">源socket</param>
  355. /// <param name="msg">消息的字节数组</param>
  356. /// <returns>返回发送字节个数</returns>
  357. public static int SendFixData(this Socket socket, byte[] msg)
  358. {
  359. int size = msg.Length; //要发送字节长度
  360. int offset = 0; //已经发送长度
  361. int dataleft = size; //剩下字符
  362. int senddata = m_maxpacket; //每次发送大小
  363. while (true)
  364. {
  365. //如过剩下的字节数 小于 每次发送字节数
  366. if (dataleft < senddata)
  367. senddata = dataleft;
  368. int count = socket.Send(msg, offset, senddata, SocketFlags.None);
  369. offset += count;
  370. dataleft -= count;
  371. if (offset == size)
  372. break;
  373. }
  374. return offset;
  375. }
  376. /// <summary>
  377. /// 发送变长信息 格式 包头(包头占8位) + 正文
  378. /// </summary>
  379. /// <param name="socket">发送方socket对象</param>
  380. /// <param name="contact">发送文本</param>
  381. /// <returns>发送的数据内容长度</returns>
  382. public static int SendVarData(this Socket socket, string contact)
  383. {
  384. //得到字符长度
  385. int size = Encoding.UTF8.GetBytes(contact).Length;
  386. //包头字符
  387. string length = GetSendPacketLengthStr(size);
  388. //包头 + 正文
  389. byte[] sendbyte = Encoding.UTF8.GetBytes(length + contact);
  390. //发送
  391. return SendFixData(socket, sendbyte);
  392. }
  393. /// <summary>
  394. /// 发送变成信息
  395. /// </summary>
  396. /// <param name="socket">发送方socket对象</param>
  397. /// <param name="bytes">消息的 字节数组</param>
  398. /// <returns>消息长度</returns>
  399. public static int SendVarData(this Socket socket, byte[] bytes)
  400. {
  401. //得到包头字节
  402. int size = bytes.Length;
  403. string length = GetSendPacketLengthStr(size);
  404. byte[] lengthbyte = Encoding.UTF8.GetBytes(length);
  405. //发送包头
  406. SendFixData(socket, lengthbyte); //因为不知道正文是什么编码所以没有合并
  407. //发送正文
  408. return SendFixData(socket, bytes);
  409. }
  410. /// <summary>
  411. /// 发送T类型对象,序列化
  412. /// </summary>
  413. /// <typeparam name="T">T类型</typeparam>
  414. /// <param name="socket">发送方的socket对象</param>
  415. /// <param name="obj">T类型对象,必须是可序列化的</param>
  416. /// <returns>消息长度</returns>
  417. public static int SendSerializeObject<T>(this Socket socket, T obj)
  418. {
  419. byte[] bytes = SerializeObject(obj);
  420. return SendVarData(socket, bytes);
  421. }
  422. /// <summary>
  423. /// 发送文件
  424. /// </summary>
  425. /// <param name="socket">socket对象</param>
  426. /// <param name="path">文件路径</param>
  427. /// <param name="issend">是否发送文件(头)信息,如果当前知道文件[大小,名称]则为false</param>
  428. /// <param name="progress">处理过程</param>
  429. /// <returns>处理结果</returns>
  430. public static bool SendFile(this Socket socket, string path, bool issend, Action<int> progress)
  431. {
  432. bool ret = false;
  433. if (File.Exists(path))
  434. {
  435. FileInfo fileinfo = new FileInfo(path);
  436. string filename = fileinfo.Name;
  437. long length = fileinfo.Length;
  438. //发送文件信息
  439. if (issend)
  440. SendVarData(socket, filename + "|" + length);
  441. //发送文件
  442. long offset = 0;
  443. byte[] b = new byte[m_maxpacket];
  444. int mark = 0;
  445. using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
  446. {
  447. int senddata = b.Length;
  448. long i = length;
  449. //循环读取发送
  450. while (true)
  451. {
  452. int count = fs.Read(b, 0, senddata);
  453. if (count > 0)
  454. {
  455. socket.Send(b, 0, count, SocketFlags.None);
  456. offset += count;
  457. mark = 0;
  458. }
  459. else
  460. {
  461. mark++;
  462. if (mark == 10)
  463. break;
  464. }
  465. if (progress != null)
  466. progress(Convert.ToInt32(Convert.ToDouble(offset) / Convert.ToDouble(length) * 100));
  467. if (offset == length)
  468. break;
  469. Thread.Sleep(50); //设置等待时间,以免粘包
  470. }
  471. }
  472. }
  473. return ret;
  474. }
  475. /// <summary>
  476. /// 发送文件,不需要进度信息
  477. /// </summary>
  478. /// <param name="socket">socket对象</param>
  479. /// <param name="path">文件路径</param>
  480. /// <param name="issend">是否发生(头)信息</param>
  481. /// <returns>处理结果</returns>
  482. public static bool SendFile(this Socket socket, string path, bool issend)
  483. {
  484. return SendFile(socket, path, issend, null);
  485. }
  486. /// <summary>
  487. /// 发送文件,不需要进度信息和(头)信息
  488. /// </summary>
  489. /// <param name="socket">socket对象</param>
  490. /// <param name="path">文件路径</param>
  491. /// <returns>处理结果</returns>
  492. public static bool SendFile(this Socket socket, string path)
  493. {
  494. return SendFile(socket, path, false, null);
  495. }
  496. private static byte[] SerializeObject(object obj)
  497. {
  498. IFormatter format = new BinaryFormatter();
  499. MemoryStream stream = new MemoryStream();
  500. format.Serialize(stream, obj);
  501. byte[] ret = stream.ToArray();
  502. stream.Close();
  503. return ret;
  504. }
  505. private static string GetSendPacketLengthStr(int size)
  506. {
  507. string length = size + "********"; //得到size的长度
  508. return length.Substring(0, 8); //截取前前8位
  509. }
  510. #endregion
  511. }
  512. }