frmClient.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using HPSocketCS;
  10. using System.Runtime.InteropServices;
  11. namespace TcpPullClientNS
  12. {
  13. public enum AppState
  14. {
  15. Starting, Started, Stoping, Stoped, Error
  16. }
  17. public partial class frmClient : Form
  18. {
  19. private AppState appState = AppState.Stoped;
  20. private delegate void ConnectUpdateUiDelegate();
  21. private delegate void SetAppStateDelegate(AppState state);
  22. private delegate void ShowMsg(string msg);
  23. private ShowMsg AddMsgDelegate;
  24. HPSocketCS.TcpPullClient client = new HPSocketCS.TcpPullClient();
  25. int id = 0;
  26. // 包头大小
  27. int pkgHeaderSize = Marshal.SizeOf(new PkgHeader());
  28. PkgInfo pkgInfo = new PkgInfo();
  29. public frmClient()
  30. {
  31. InitializeComponent();
  32. }
  33. private void frmClient_Load(object sender, EventArgs e)
  34. {
  35. try
  36. {
  37. // 加个委托显示msg,因为on系列都是在工作线程中调用的,ui不允许直接操作
  38. AddMsgDelegate = new ShowMsg(AddMsg);
  39. pkgInfo.IsHeader = true;
  40. pkgInfo.Length = pkgHeaderSize;
  41. // 设置client事件
  42. client.OnPrepareConnect += new TcpClientEvent.OnPrepareConnectEventHandler(OnPrepareConnect);
  43. client.OnConnect += new TcpClientEvent.OnConnectEventHandler(OnConnect);
  44. client.OnSend += new TcpClientEvent.OnSendEventHandler(OnSend);
  45. client.OnReceive += new TcpPullClientEvent.OnReceiveEventHandler(OnReceive);
  46. client.OnClose += new TcpClientEvent.OnCloseEventHandler(OnClose);
  47. SetAppState(AppState.Stoped);
  48. }
  49. catch (Exception ex)
  50. {
  51. SetAppState(AppState.Error);
  52. AddMsg(ex.Message);
  53. }
  54. }
  55. private void btnStart_Click(object sender, EventArgs e)
  56. {
  57. try
  58. {
  59. String ip = this.txtIpAddress.Text.Trim();
  60. ushort port = ushort.Parse(this.txtPort.Text.Trim());
  61. // 写在这个位置是上面可能会异常
  62. SetAppState(AppState.Starting);
  63. AddMsg(string.Format("$Client Starting ... -> ({0}:{1})", ip, port));
  64. if (client.Connect(ip, port, this.cbxAsyncConn.Checked))
  65. {
  66. if (cbxAsyncConn.Checked == false)
  67. {
  68. SetAppState(AppState.Started);
  69. }
  70. }
  71. else
  72. {
  73. SetAppState(AppState.Stoped);
  74. throw new Exception(string.Format("$Client Start Error -> {0}({1})", client.ErrorMessage, client.ErrorCode));
  75. }
  76. }
  77. catch (Exception ex)
  78. {
  79. AddMsg(ex.Message);
  80. }
  81. }
  82. private void btnStop_Click(object sender, EventArgs e)
  83. {
  84. // 停止服务
  85. AddMsg("$Server Stop");
  86. if (client.Stop())
  87. {
  88. SetAppState(AppState.Stoped);
  89. }
  90. else
  91. {
  92. AddMsg(string.Format("$Stop Error -> {0}({1})", client.ErrorMessage, client.ErrorCode));
  93. }
  94. }
  95. private void btnSend_Click(object sender, EventArgs e)
  96. {
  97. IntPtr bufferPtr = IntPtr.Zero;
  98. try
  99. {
  100. string send = this.txtSend.Text;
  101. if (send.Length == 0)
  102. {
  103. return;
  104. }
  105. // 封包体
  106. byte[] bodyBytes = Encoding.Default.GetBytes(send);
  107. // 封包头
  108. PkgHeader header = new PkgHeader();
  109. header.Id = ++id;
  110. header.BodySize = bodyBytes.Length;
  111. byte[] headerBytes = client.StructureToByte<PkgHeader>(header);
  112. // 组合最终发送的封包 (封包头+封包体)
  113. byte[] sendBytes = GetSendBuffer(headerBytes, bodyBytes);
  114. // 发送
  115. IntPtr connId = client.ConnectionId;
  116. if (client.Send(sendBytes, sendBytes.Length))
  117. {
  118. AddMsg(string.Format("$ ({0}) Send OK --> {1}", connId, send));
  119. }
  120. else
  121. {
  122. AddMsg(string.Format("$ ({0}) Send Fail --> {1} ({2})", connId, send, sendBytes.Length));
  123. }
  124. }
  125. catch (Exception)
  126. {
  127. }
  128. finally
  129. {
  130. if (bufferPtr != IntPtr.Zero)
  131. {
  132. Marshal.FreeHGlobal(bufferPtr);
  133. }
  134. }
  135. }
  136. private void lbxMsg_KeyPress(object sender, KeyPressEventArgs e)
  137. {
  138. // 清理listbox
  139. if (e.KeyChar == 'c' || e.KeyChar == 'C')
  140. {
  141. this.lbxMsg.Items.Clear();
  142. }
  143. }
  144. void ConnectUpdateUi()
  145. {
  146. if (this.cbxAsyncConn.Checked == true)
  147. {
  148. SetAppState(AppState.Started);
  149. }
  150. }
  151. HandleResult OnPrepareConnect(TcpClient sender, uint socket)
  152. {
  153. return HandleResult.Ok;
  154. }
  155. HandleResult OnConnect(TcpClient sender)
  156. {
  157. // 已连接 到达一次
  158. // 如果是异步联接,更新界面状态
  159. this.Invoke(new ConnectUpdateUiDelegate(ConnectUpdateUi));
  160. AddMsg(string.Format(" > [{0},OnConnect]", sender.ConnectionId));
  161. return HandleResult.Ok;
  162. }
  163. HandleResult OnSend(TcpClient sender, byte[] bytes)
  164. {
  165. // 客户端发数据了
  166. AddMsg(string.Format(" > [{0},OnSend] -> ({1} bytes)", sender.ConnectionId, bytes.Length));
  167. return HandleResult.Ok;
  168. }
  169. HandleResult OnReceive(TcpPullClient sender, int length)
  170. {
  171. // 数据到达了
  172. // 需要长度
  173. int required = pkgInfo.Length;
  174. // 剩余大小
  175. int remain = length;
  176. while (remain >= required)
  177. {
  178. IntPtr bufferPtr = IntPtr.Zero;
  179. try
  180. {
  181. remain -= required;
  182. bufferPtr = Marshal.AllocHGlobal(required);
  183. if (sender.Fetch(bufferPtr, required) == FetchResult.Ok)
  184. {
  185. if (pkgInfo.IsHeader == true)
  186. {
  187. PkgHeader header = (PkgHeader)Marshal.PtrToStructure(bufferPtr, typeof(PkgHeader));
  188. // 调试信息
  189. Console.WriteLine("[Client] head -> Id: {0}, BodySize: {1}\r\n", header.Id, header.BodySize);
  190. required = header.BodySize;
  191. }
  192. else
  193. {
  194. // 调试信息
  195. string recvString = Marshal.PtrToStringAnsi(bufferPtr, required);
  196. Console.WriteLine("[Client] body -> text: {0}\r\n", recvString);
  197. required = pkgHeaderSize;
  198. }
  199. AddMsg(string.Format(" > [{0},OnReceive] -> ({1} bytes)", sender.ConnectionId, pkgInfo.Length));
  200. pkgInfo.IsHeader = !pkgInfo.IsHeader;
  201. pkgInfo.Length = required;
  202. }
  203. }
  204. catch
  205. {
  206. return HandleResult.Error;
  207. }
  208. finally
  209. {
  210. if (bufferPtr != IntPtr.Zero)
  211. {
  212. Marshal.FreeHGlobal(bufferPtr);
  213. bufferPtr = IntPtr.Zero;
  214. }
  215. }
  216. }
  217. return HandleResult.Ok;
  218. }
  219. HandleResult OnClose(TcpClient sender, SocketOperation enOperation, int errorCode)
  220. {
  221. if(errorCode == 0)
  222. // 连接关闭了
  223. AddMsg(string.Format(" > [{0},OnClose]", sender.ConnectionId));
  224. else
  225. // 出错了
  226. AddMsg(string.Format(" > [{0},OnError] -> OP:{1},CODE:{2}", sender.ConnectionId, enOperation, errorCode));
  227. // 通知界面,只处理了连接错误,也没进行是不是连接错误的判断,所以有错误就会设置界面
  228. // 生产环境请自己控制
  229. this.Invoke(new SetAppStateDelegate(SetAppState), AppState.Stoped);
  230. return HandleResult.Ok;
  231. }
  232. /// <summary>
  233. /// 设置程序状态
  234. /// </summary>
  235. /// <param name="state"></param>
  236. void SetAppState(AppState state)
  237. {
  238. appState = state;
  239. this.btnStart.Enabled = (appState == AppState.Stoped);
  240. this.btnStop.Enabled = (appState == AppState.Started);
  241. this.txtIpAddress.Enabled = (appState == AppState.Stoped);
  242. this.txtPort.Enabled = (appState == AppState.Stoped);
  243. this.cbxAsyncConn.Enabled = (appState == AppState.Stoped);
  244. this.btnSend.Enabled = (appState == AppState.Started);
  245. }
  246. /// <summary>
  247. /// 往listbox加一条项目
  248. /// </summary>
  249. /// <param name="msg"></param>
  250. void AddMsg(string msg)
  251. {
  252. if (this.lbxMsg.InvokeRequired)
  253. {
  254. // 很帅的调自己
  255. this.lbxMsg.Invoke(AddMsgDelegate, msg);
  256. }
  257. else
  258. {
  259. if (this.lbxMsg.Items.Count > 100)
  260. {
  261. this.lbxMsg.Items.RemoveAt(0);
  262. }
  263. this.lbxMsg.Items.Add(msg);
  264. this.lbxMsg.TopIndex = this.lbxMsg.Items.Count - (int)(this.lbxMsg.Height / this.lbxMsg.ItemHeight);
  265. }
  266. }
  267. private void frmClient_FormClosed(object sender, FormClosedEventArgs e)
  268. {
  269. client.Destroy();
  270. }
  271. private byte[] GetSendBuffer(byte[] headerBytes, byte[] bodyBytes)
  272. {
  273. IntPtr ptr = IntPtr.Zero;
  274. try
  275. {
  276. int bufferSize = headerBytes.Length + bodyBytes.Length;
  277. ptr = Marshal.AllocHGlobal(bufferSize);
  278. // 拷贝包头到缓冲区首部
  279. Marshal.Copy(headerBytes, 0, ptr, headerBytes.Length);
  280. // 拷贝包体到缓冲区剩余部分
  281. Marshal.Copy(bodyBytes, 0, ptr + headerBytes.Length, bodyBytes.Length);
  282. byte[] bytes = new byte[bufferSize];
  283. Marshal.Copy(ptr, bytes, 0, bufferSize);
  284. return bytes;
  285. }
  286. finally
  287. {
  288. if (ptr != IntPtr.Zero)
  289. {
  290. Marshal.FreeHGlobal(ptr);
  291. }
  292. }
  293. }
  294. }
  295. [StructLayout(LayoutKind.Sequential)]
  296. public class PkgHeader
  297. {
  298. public int Id;
  299. public int BodySize;
  300. }
  301. [StructLayout(LayoutKind.Sequential)]
  302. public class PkgInfo
  303. {
  304. public bool IsHeader;
  305. public int Length;
  306. }
  307. }