使用C#进行点对点通讯和文件传输

Posted on 2006-12-21 08:55  flourish  阅读(501)  评论(0)    收藏  举报

最近一个项目要用到点对点文件传输,俺就到处找资料写程序,最后终于完成了,为了让别人少走些弯路,俺决定将俺程序中最重要的部分贡献出来,希望对大家有所帮助。



俺的程序分三部分,包括发送部分、接受部分和一个两者共享的通讯基类,这个基类才是俺心血的结晶:)

  1. 一、通讯基类  
  2. using System;  
  3. using System.Net.Sockets;  
  4. using System.Net ;  
  5. using System.IO ;  
  6. using System.Windows.Forms;  
  7. using System.Text;  
  8. namespace BaseClass  
  9. {  
  10. /// <summary>  
  11. /// 传送信息的格式为 给定长度的命令部分+给定长度的命令注释部分+可变长度的长度信息+可变长度的信息部分  
  12. /// </summary>  
  13. public class CommunClass  
  14. {  
  15. public CommunClass()  
  16. {  
  17. //  
  18. // TODO: 在此处添加构造函数逻辑  
  19. //  
  20. }  
  21. /// <summary>  
  22. /// 命令部分的长度  
  23. /// </summary>  
  24. private static readonly int CMDLEN = 50 ;  
  25. /// <summary>  
  26. /// 命令注释部分的长度  
  27. /// </summary>  
  28. private static readonly int DESCLEN = 100 ;  
  29. /// <summary>  
  30. /// 可变长度的长度信息部分所占的字节数  
  31. /// </summary>  
  32. private static readonly int DYNAMICLENGTHLEN = 10 ;  
  33. /// <summary>  
  34. /// 每次处理可变信息部分的长度  
  35. /// </summary>  
  36. private static readonly int DEALLEN = 1024 ;  
  37. /// <summary>  
  38. /// /应答的最大长度  
  39. /// </summary>  
  40. private static readonly int RESPONLEN = 20 ;  
  41. /// <summary>  
  42. /// 用于填充命令或注释不足长度部分的字符  
  43. /// </summary>  
  44. private static readonly char FILLCHAR = ''^'' ;  
  45. /// <summary>  
  46. /// 成功发送一部分数据后的回调方法(也可以认为是触发的事件,但严格来说还不是)  
  47. /// </summary>  
  48. public delegate void OnSend(int iTotal,int iSending) ;  
  49. /// <summary>  
  50. /// 根据给定的服务器和端口号建立连接  
  51. /// </summary>  
  52. /// <param name="strHost">服务器名</param>  
  53. /// <param name="iPort">端口号</param>  
  54. /// <returns></returns>  
  55. public static Socket ConnectToServer(string strHost,int iPort)  
  56. {  
  57. try  
  58. {  
  59. IPAddress ipAddress = Dns.Resolve(strHost).AddressList[0];  
  60. IPEndPoint ipPoint = new IPEndPoint(ipAddress,iPort) ;  
  61. Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp) ;  
  62. s.Connect(ipPoint) ;  
  63. return s ;  
  64. }  
  65. catch (Exception e)  
  66. {  
  67. throw (new Exception("建立到服务器的连接出错" + e.Message)) ;  
  68. }  
  69. }  
  70. /// <summary>  
  71. /// 将文本写到Socket中  
  72. /// </summary>  
  73. /// <param name="s">要发送信息的Socket</param>  
  74. /// <param name="strInfo">要发送的信息</param>  
  75. /// <returns>是否成功</returns>  
  76. public static bool WriteTextToSocket(Socket s,string strInfo)  
  77. {  
  78. byte [] buf = Encoding.UTF8.GetBytes(strInfo) ;  
  79. try  
  80. {  
  81. s.Send(buf,0,buf.Length,SocketFlags.None) ;  
  82. return true ;  
  83. }  
  84. catch(Exception err)  
  85. {  
  86. MessageBox.Show("发送文本失败!"+err.Message) ;  
  87. return false ;  
  88. }  
  89. }  
  90. /// <summary>  
  91. /// 将命令文本写到Socket中  
  92. /// </summary>  
  93. /// <param name="s">要发送命令文本的Socket</param>  
  94. /// <param name="strInfo">要发送的命令文本</param>  
  95. /// <returns>是否成功</returns>  
  96. public static bool WriteCommandToSocket(Socket s,string strCmd)  
  97. {  
  98. if (strCmd == "")  
  99. strCmd = "NOP" ;  
  100. strCmd = strCmd.PadRight(CMDLEN,FILLCHAR) ;  
  101. return WriteTextToSocket(s,strCmd) ;  
  102. }  
  103. /// <summary>  
  104. /// 将命令注释写到Socket中  
  105. /// </summary>  
  106. /// <param name="s">要发送命令注释的Socket</param>  
  107. /// <param name="strInfo">要发送的命令注释</param>  
  108. /// <returns>是否成功</returns>  
  109. public static bool WriteCommandDescToSocket(Socket s,string strDesc)  
  110. {  
  111. if (strDesc == "")  
  112. strDesc = "0" ;  
  113. strDesc = strDesc.PadRight(DESCLEN,FILLCHAR) ;  
  114. return WriteTextToSocket(s,strDesc) ;  
  115. }  
  116. /// <summary>  
  117. /// 发送可变信息的字节数  
  118. /// </summary>  
  119. /// <param name="s">要发送字节数的Socket</param>  
  120. /// <param name="iLen">字节数</param>  
  121. /// <returns>是否成功</returns>  
  122. public static bool WriteDynamicLenToSocket(Socket s,int iLen)  
  123. {  
  124. string strLen = iLen.ToString().PadRight(DYNAMICLENGTHLEN,FILLCHAR) ;  
  125. return WriteTextToSocket(s,strLen) ;  
  126. }  
  127. /// <summary>  
  128. /// 将缓存的指定部分发送到Socket  
  129. /// </summary>  
  130. /// <param name="s">要发送缓存的Socket</param>  
  131. /// <param name="buf">要发送的缓存</param>  
  132. /// <param name="iStart">要发送缓存的起始位置</param>  
  133. /// <param name="iCount">要发送缓存的字节数</param>  
  134. /// <param name="iBlock">每次发送的字节说</param>  
  135. /// <param name="SendSuccess">每次发送成功后的回调函数</param>  
  136. /// <returns>是否发送成功</returns>  
  137. public static bool WriteBufToSocket(Socket s,byte [] buf,int iStart,int iCount,int iBlock,OnSend SendSuccess)  
  138. {  
  139. int iSended = 0 ;  
  140. int iSending = 0 ;  
  141. while(iSended<iCount)  
  142. {  
  143. if (iSended + iBlock <= iCount)  
  144. iSending = iBlock ;  
  145. else  
  146. iSending = iCount - iSended ;  
  147. s.Send(buf,iStart+iSended,iSending,SocketFlags.None) ;  
  148. iSended += iSending ;  
  149. if (ReadResponsionFromSocket(s)=="OK")  
  150. if (SendSuccess != null)  
  151. SendSuccess(iCount,iSended) ;  
  152. else  
  153. return false;  
  154. }  
  155. return true ;  
  156. }  
  157. /// <summary>  
  158. /// 将长度不固定文本发送到socket  
  159. /// </summary>  
  160. /// <param name="s">要发送文本的Socket</param>  
  161. /// <param name="strText">要发送的文本</param>  
  162. /// <param name="OnSendText">成功发送一部分文本后的回调函数</param>  
  163. /// <param name="settextlen">得到文本长度的回调函数</param>  
  164. /// <returns></returns>  
  165. public static bool WriteDynamicTextToSocket(Socket s,string strText,  
  166. OnSend OnSendText)  
  167. {  
  168. byte [] buf = Encoding.UTF8.GetBytes(strText) ;  
  169. int iLen = buf.Length ;  
  170. try  
  171. {  
  172. WriteDynamicLenToSocket(s,iLen) ;  
  173. return WriteBufToSocket(s,buf,0,iLen,DEALLEN,OnSendText) ;  
  174. }  
  175. catch(Exception err)  
  176. {  
  177. MessageBox.Show("发送文本失败!"+err.Message) ;  
  178. return false ;  
  179. }  
  180. }  
  181. /// <summary>  
  182. /// 将文件写到Socket  
  183. /// </summary>  
  184. /// <param name="s">要发送文件的Socket</param>  
  185. /// <param name="strFile">要发送的文件</param>  
  186. /// <returns>是否成功</returns>  
  187. public static bool WriteFileToSocket(Socket s,string strFile,  
  188. OnSend OnSendFile)  
  189. {  
  190. FileStream fs = new FileStream(strFile,FileMode.Open,FileAccess.Read,FileShare.Read) ;  
  191. int iLen = (int)fs.Length ;  
  192. WriteDynamicLenToSocket(s,iLen) ;  
  193. byte [] buf = new byte[iLen] ;  
  194. try  
  195. {  
  196. fs.Read(buf,0,iLen) ;  
  197. return WriteBufToSocket(s,buf,0,iLen,DEALLEN,OnSendFile) ;  
  198. }  
  199. catch(Exception err)  
  200. {  
  201. MessageBox.Show("发送文件失败!"+err.Message) ;  
  202. return false ;  
  203. }  
  204. finally  
  205. {  
  206. fs.Close() ;  
  207. }  
  208. }  
  209. /// <summary>  
  210. /// 对方对自己消息的简单回应  
  211. /// </summary>  
  212. /// <param name="s"></param>  
  213. /// <returns></returns>  
  214. public static string ReadResponsionFromSocket( Socket s)  
  215. {  
  216. byte [] bufCmd = new byte[RESPONLEN] ;  
  217. int iCount = s.Receive(bufCmd) ;  
  218. string strRespon = Encoding.UTF8.GetString(bufCmd,0,iCount) ;  
  219. return strRespon ;  
  220. }  
  221. /// <summary>  
  222. /// 从Socket读取命令  
  223. /// </summary>  
  224. /// <param name="s">要读取命令的Socket</param>  
  225. /// <returns>读取的命令</returns>  
  226. public static string ReadCommandFromSocket( Socket s)  
  227. {  
  228. byte [] bufCmd = new byte[CMDLEN] ;  
  229. int iCount = s.Receive(bufCmd,0,CMDLEN,SocketFlags.Partial) ;  
  230. string strCommand = Encoding.UTF8.GetString(bufCmd,0,CMDLEN) ;  
  231. return strCommand = strCommand.TrimEnd(FILLCHAR) ;  
  232. }  
  233. /// <summary>  
  234. /// 读取命令注释  
  235. /// </summary>  
  236. /// <param name="s">要读取命令注释的Socket</param>  
  237. /// <returns>读取的命令注释</returns>  
  238. public static string ReadCommandDescFromSocket( Socket s)  
  239. {  
  240. byte [] bufCmd = new byte[DESCLEN] ;  
  241. int iCount = s.Receive(bufCmd,0,DESCLEN,SocketFlags.Partial) ;  
  242. string strCommand = Encoding.UTF8.GetString(bufCmd,0,DESCLEN) ;  
  243. return strCommand = strCommand.TrimEnd(FILLCHAR) ;  
  244. }  
  245. /// <summary>  
  246. /// 读取可变部分的长度  
  247. /// </summary>  
  248. /// <param name="s">要读取可变部分长度的Socket</param>  
  249. /// <returns>读取的可变部分的长度</returns>  
  250. public static int ReadDynamicLenFromSocket( Socket s)  
  251. {  
  252. byte [] bufCmd = new byte[DYNAMICLENGTHLEN] ;  
  253. int iCount = s.Receive(bufCmd,0,DYNAMICLENGTHLEN,SocketFlags.Partial) ;  
  254. string strCommand = Encoding.UTF8.GetString(bufCmd,0,DYNAMICLENGTHLEN) ;  
  255. return int.Parse(strCommand.TrimEnd(FILLCHAR)) ;  
  256. }  
  257. /// <summary>  
  258. /// 读取文本形式的可变信息  
  259. /// </summary>  
  260. /// <param name="s">要读取可变信息的Socket</param>  
  261. /// <returns>读取的可变信息</returns>  
  262. public static string ReadDynamicTextFromSocket( Socket s)  
  263. {  
  264. int iLen = ReadDynamicLenFromSocket(s) ;  
  265. byte [] buf = new byte[iLen] ;  
  266. string strInfo = "" ;  
  267. int iReceiveded = 0 ;  
  268. int iReceiveing = 0 ;  
  269. while(iReceiveded<iLen)  
  270. {  
  271. if (iReceiveded + DEALLEN <= iLen)  
  272. iReceiveing = DEALLEN ;  
  273. else  
  274. iReceiveing = iLen - iReceiveded ;  
  275. s.Receive(buf,iReceiveded,iReceiveing,SocketFlags.None) ;  
  276. CommunClass.WriteTextToSocket(s,"OK") ;  
  277. iReceiveded+= iReceiveing ;  
  278. }  
  279. strInfo = Encoding.UTF8.GetString(buf,0,iLen) ;  
  280. return strInfo ;  
  281. }  
  282. /// <summary>  
  283. /// 读取文件形式的可变信息  
  284. /// </summary>  
  285. /// <param name="s">要读取可变信息的Socket</param>  
  286. /// <param name="strFile">读出后的文件保存位置</param>  
  287. /// <returns>是否读取成功</returns>  
  288. public static bool ReadDynamicFileFromSocket( Socket s,string strFile)  
  289. {  
  290. int iLen = ReadDynamicLenFromSocket(s) ;  
  291. byte [] buf = new byte[iLen] ;  
  292. FileStream fs = new FileStream(strFile,FileMode.Create,FileAccess.Write) ;  
  293. try  
  294. {  
  295. int iReceiveded = 0 ;  
  296. int iReceiveing = 0 ;  
  297. while(iReceiveded<iLen)  
  298. {  
  299. if (iReceiveded + DEALLEN <= iLen)  
  300. iReceiveing = DEALLEN ;  
  301. else  
  302. iReceiveing = iLen - iReceiveded ;  
  303. s.Receive(buf,iReceiveded,iReceiveing,SocketFlags.None) ;  
  304. CommunClass.WriteTextToSocket(s,"OK") ;  
  305. iReceiveded+= iReceiveing ;  
  306. }  
  307. fs.Write(buf,0,iLen) ;  
  308. return true ;  
  309. }  
  310. catch(Exception err)  
  311. {  
  312. MessageBox.Show("接收文件失败"+err.Message) ;  
  313. return false ;  
  314. }  
  315. finally  
  316. {  
  317. fs.Close() ;  
  318. }  
  319. }  
  320. }//end class  
  321. }//end namespace  
  322. 上面是俺的通讯基础类,有了这个类,再进行发送接受还不是小菜一碟吗?  
  323. 上面介绍了通讯的基类,下面就是使用那个类进行发送和接收的部分:  
  324. 二、发送部分:  
  325. 发送咱们使用了多线程,可以同时进行多个任务,比如发送文件、发送文本等,互不影响:  
  326. 发送文本方法:  
  327. private void StartSendText(string strHost,int iPort,string strInfo)  
  328. {  
  329. SendText stText = new SendText(strHost,iPort,strInfo,new CommunClass.OnSend(OnSendDrawProgress)) ;  
  330. StartThread(new ThreadStart(stText.Send)) ;  
  331. }  
  332. 下面是他调用用到的一些方法:  
  333. 开始一个线程  
  334. private void StartThread(ThreadStart target)  
  335. {  
  336. Thread doStep = new Thread(target) ;  
  337. doStep.IsBackground = true ;  
  338. doStep.Start() ;  
  339. }  
  340. 发送一部分(本文设置的是1024字节)成功后的回调方法  
  341. public void OnSendDrawProgress(int iTotal,int iSending)  
  342. {  
  343. if (iTotal != pbMain.Maximum)  
  344. pbMain.Maximum = iTotal ;  
  345. pbMain.Value = iSending ;  
  346. }  
  347. 因为使用的是线程,所以发送文本使用的是一个发送文本类的方法,该类如下:  
  348. public class SendText  
  349. {  
  350. private string Host ;  
  351. private int Port ;  
  352. private string Info ;  
  353. private CommunClass.OnSend onsend ;  
  354. public SendText(string strHost,int iPort,string strInfo,  
  355. CommunClass.OnSend onSend)  
  356. {  
  357. Host = strHost ;  
  358. Port = iPort ;  
  359. Info = strInfo ;  
  360. onsend = onSend ;  
  361. }  
  362. public void Send()  
  363. {  
  364. Socket s = null ;  
  365. try  
  366. {  
  367. s = CommunClass.ConnectToServer(Host,Port) ;  
  368. CommunClass.WriteCommandToSocket(s,"SENDTEXT") ;  
  369. CommunClass.WriteCommandDescToSocket(s,"") ;  
  370. CommunClass.WriteDynamicTextToSocket(s,Info,onsend) ;  
  371. }  
  372. catch (Exception e)  
  373. {  
  374. MessageBox.Show(e.Message) ;  
  375. }  
  376. finally  
  377. {  
  378. if (s != null)  
  379. s.Close() ;  
  380. }  
  381. }  
  382. }//end class  
  383. 这样就可以使用一个线程发送文本了。  
  384. 发送文件的方法也类似:  
  385. private void StartSendFile(string strHost,int iPort,string strFile)  
  386. {  
  387. SendFile sfFile = new SendFile(strHost,iPort,strFile,this.pbMain) ;  
  388. pbMain.Value = 0 ;  
  389. StartThread(new ThreadStart(sfFile.Send)) ;  
  390. }  
  391. 发送文件的类:  
  392. public class SendFile  
  393. {  
  394. private string Host ;  
  395. private int Port ;  
  396. private string FileToSend ;  
  397. private ProgressBar pbar;  
  398. public SendFile(string strHost,int iPort,string strFile,ProgressBar pbMain)  
  399. {  
  400. Host = strHost ;  
  401. Port = iPort ;  
  402. FileToSend = strFile ;  
  403. pbar = pbMain ;  
  404. }  
  405. public void Send()  
  406. {  
  407. Socket s = null ;  
  408. try  
  409. {  
  410. s = CommunClass.ConnectToServer(Host,Port) ;  
  411. CommunClass.WriteCommandToSocket(s,"SENDFILE") ;  
  412. CommunClass.WriteCommandDescToSocket(s,"") ;  
  413. CommunClass.WriteFileToSocket(s,FileToSend,new CommunClass.OnSend(OnSendDrawProgress)) ;  
  414. }  
  415. catch (Exception e)  
  416. {  
  417. MessageBox.Show(e.Message) ;  
  418. }  
  419. finally  
  420. {  
  421. if (s != null)  
  422. s.Close() ;  
  423. }  
  424. }  
  425. public void OnSendDrawProgress(int iTotal,int iSending)  
  426. {  
  427. if (iTotal != pbar.Maximum)  
  428. pbar.Maximum = iTotal ;  
  429. pbar.Value = iSending ;  
  430. }  
  431. }//end class  
  432. 当然,你发送一个命令让服务器端启动一个程序(靠,这不成木马了吗?)也可以:  
  433. 俺这里只给出一部分代码,其余的您自己可以发挥以下:  
  434. public class ExeCuteFile  
  435. {  
  436. private string Host ;  
  437. private int Port ;  
  438. private string FileName ;  
  439. private string cmdParam ;  
  440. public ExeCuteFile(string strHost,int iPort,string strFileName,string strCmdParam)  
  441. {  
  442. Host = strHost ;  
  443. Port = iPort ;  
  444. FileName = strFileName ;  
  445. cmdParam = strCmdParam ;  
  446. }  
  447. public void Send()  
  448. {  
  449. Socket s = null ;  
  450. try  
  451. {  
  452. s = CommunClass.ConnectToServer(Host,Port) ;  
  453. CommunClass.WriteCommandToSocket(s,"EXECUTEFILE") ;  
  454. CommunClass.WriteCommandDescToSocket(s,FileName) ;  
  455. CommunClass.WriteDynamicTextToSocket(s,"",null) ;  
  456. MessageBox.Show(CommunClass.ReadDynamicTextFromSocket(s)) ;  
  457. }  
  458. catch (Exception e)  
  459. {  
  460. MessageBox.Show(e.Message) ;  
  461. }  
  462. finally  
  463. {  
  464. if (s != null)  
  465. s.Close() ;  
  466. }  
  467. }  
  468. }  
  469. 三、下面是服务器端接受信息的代码:  
  470. 创建监听:  
  471. /// <summary>  
  472. /// 再给定的主机和端口上创建监听程序  
  473. /// </summary>  
  474. /// <param name="strAddress"></param>  
  475. /// <param name="iPort"></param>  
  476. private void BuildingServer(string strAddress,int iPort)  
  477. {  
  478. IPAddress ipAddress = Dns.Resolve(strAddress).AddressList[0];  
  479. try  
  480. {  
  481. listener = new TcpListener(ipAddress, iPort);  
  482. }  
  483. catch ( Exception e)  
  484. {  
  485. AddInfo(e.Message) ;  
  486. }  
  487. }  
  488. 开始监听:  
  489. /// <summary>  
  490. /// 开始监听  
  491. /// </summary>  
  492. private void StartListen()  
  493. {  
  494. bool done = false;  
  495. listener.Start();  
  496. while (!done)  
  497. {  
  498. Socket s = listener.AcceptSocket() ;  
  499. if(s != null)  
  500. {  
  501. DealWithSocket dws = new DealWithSocket(s,this.tbLog) ;  
  502. StartThread(new ThreadStart(dws.DealWith)) ;  
  503. }  
  504. }  
  505. }  
  506. private void StartThread(ThreadStart target)  
  507. {  
  508. Thread doStep = new Thread(target) ;  
  509. doStep.IsBackground = true ;  
  510. doStep.Start() ;  
  511. }  
  512. 开始监听后,对于每一个监听到的客户端的连接都用一个单独的线程来处理,处理通过类DealWithSocket来完成,下面是类代码:  
  513. public class DealWithSocket  
  514. {  
  515. private Socket s = null ;  
  516. private TextBox tbLog = null ;  
  517. public DealWithSocket(Socket newSocket,TextBox tbInfo)  
  518. {  
  519. s = newSocket ;  
  520. tbLog = tbInfo ;  
  521. }  
  522. public void DealWith()  
  523. {  
  524. string strCmd = CommunClass.ReadCommandFromSocket(s) ;  
  525. string strDesc = CommunClass.ReadCommandDescFromSocket(s) ;  
  526. AddInfo(strCmd) ;  
  527. switch(strCmd)  
  528. {  
  529. case "SENDFILE" :  
  530. CommunClass.ReadDynamicFileFromSocket(s,"e:\\rrr.txt") ;  
  531. break ;  
  532. case "EXECUTEFILE" :  
  533. string strParam = CommunClass.ReadDynamicTextFromSocket(s) ;  
  534. string strResult = ExeCuteFile(strDesc,strParam) ;  
  535. CommunClass.WriteDynamicTextToSocket(s,strResult,null) ;  
  536. break ;  
  537. default:  
  538. string strDetail = CommunClass.ReadDynamicTextFromSocket(s) ;  
  539. AddInfo(strDetail) ;  
  540. break ;  
  541. }  
  542. try  
  543. {  
  544. s.Close() ;  
  545. }  
  546. catch (Exception e)  
  547. {  
  548. AddInfo(e.Message) ;  
  549. }  
  550. }  
  551. private void AddInfo(string strInfo)  
  552. {  
  553. string Info = DateTime.Now.ToLongTimeString() + " "+ strInfo +"\r\n" ;  
  554. tbLog.Text += Info ;  
  555. tbLog.Refresh() ;  
  556. }  
  557. private string ExeCuteFile(string strFileName,string strCmdParam)  
  558. {  
  559. System.Diagnostics.Process proc = new System.Diagnostics.Process() ;  
  560. proc.StartInfo.FileName = strFileName ;  
  561. proc.StartInfo.Arguments = strCmdParam ;  
  562. try  
  563. {  
  564. proc.Start() ;  
  565. return "OK" ;  
  566. }  
  567. catch(Exception err)  
  568. {  
  569. return err.Message ;  
  570. }  
  571. }  
  572. }//end class  
  573.  

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3