frmClient.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  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. using System.IO;
  12. using System.Threading;
  13. namespace SSLClientNS
  14. {
  15. public enum AppState
  16. {
  17. Starting, Started, Stoping, Stoped, Error
  18. }
  19. public enum StudentType
  20. {
  21. None, Array, List, Single,
  22. }
  23. public partial class frmClient : Form
  24. {
  25. private AppState appState = AppState.Stoped;
  26. private delegate void ConnectUpdateUiDelegate();
  27. private delegate void SetAppStateDelegate(AppState state);
  28. private delegate void ShowMsg(string msg);
  29. private ShowMsg AddMsgDelegate;
  30. HPSocketCS.TcpClient client = new HPSocketCS.TcpClient();
  31. bool isSendFile = false;
  32. StudentType studentType = StudentType.None;
  33. public frmClient()
  34. {
  35. InitializeComponent();
  36. }
  37. private void frmClient_Load(object sender, EventArgs e)
  38. {
  39. try
  40. {
  41. // 加个委托显示msg,因为on系列都是在工作线程中调用的,ui不允许直接操作
  42. AddMsgDelegate = new ShowMsg(AddMsg);
  43. // 设置client事件
  44. client.OnPrepareConnect += new TcpClientEvent.OnPrepareConnectEventHandler(OnPrepareConnect);
  45. client.OnConnect += new TcpClientEvent.OnConnectEventHandler(OnConnect);
  46. client.OnSend += new TcpClientEvent.OnSendEventHandler(OnSend);
  47. client.OnReceive += new TcpClientEvent.OnReceiveEventHandler(OnReceive);
  48. client.OnClose += new TcpClientEvent.OnCloseEventHandler(OnClose);
  49. SetAppState(AppState.Stoped);
  50. }
  51. catch (Exception ex)
  52. {
  53. SetAppState(AppState.Error);
  54. AddMsg(ex.Message);
  55. }
  56. }
  57. private void btnStart_Click(object sender, EventArgs e)
  58. {
  59. try
  60. {
  61. String ip = this.txtIpAddress.Text.Trim();
  62. ushort port = ushort.Parse(this.txtPort.Text.Trim());
  63. // 写在这个位置是上面可能会异常
  64. SetAppState(AppState.Starting);
  65. AddMsg(string.Format("$Client Starting ... -> ({0}:{1})", ip, port));
  66. if (client.Connect(ip, port, this.cbxAsyncConn.Checked))
  67. {
  68. if (cbxAsyncConn.Checked == false)
  69. {
  70. SetAppState(AppState.Started);
  71. }
  72. AddMsg(string.Format("$Client Start OK -> ({0}:{1})", ip, port));
  73. }
  74. else
  75. {
  76. SetAppState(AppState.Stoped);
  77. throw new Exception(string.Format("$Client Start Error -> {0}({1})", client.ErrorMessage, client.ErrorCode));
  78. }
  79. }
  80. catch (Exception ex)
  81. {
  82. AddMsg(ex.Message);
  83. }
  84. }
  85. private void btnStop_Click(object sender, EventArgs e)
  86. {
  87. // 停止服务
  88. AddMsg("$Server Stop");
  89. if (client.Stop())
  90. {
  91. SetAppState(AppState.Stoped);
  92. }
  93. else
  94. {
  95. AddMsg(string.Format("$Stop Error -> {0}({1})", client.ErrorMessage, client.ErrorCode));
  96. }
  97. }
  98. private void btnSend_Click(object sender, EventArgs e)
  99. {
  100. try
  101. {
  102. string send = this.txtSend.Text;
  103. if (send.Length == 0)
  104. {
  105. return;
  106. }
  107. byte[] bytes = Encoding.Default.GetBytes(send);
  108. IntPtr connId = client.ConnectionId;
  109. // 发送
  110. if (client.Send(bytes, bytes.Length))
  111. {
  112. AddMsg(string.Format("$ ({0}) Send OK --> {1}", connId, send));
  113. }
  114. else
  115. {
  116. AddMsg(string.Format("$ ({0}) Send Fail --> {1} ({2})", connId, send, bytes.Length));
  117. }
  118. }
  119. catch (Exception ex)
  120. {
  121. AddMsg(string.Format("$ Send Fail --> msg ({0})", ex.Message));
  122. }
  123. }
  124. private void btnSendSerializableObject_Click(object sender, EventArgs e)
  125. {
  126. try
  127. {
  128. // 注: 对象序列化发送数据较大
  129. if (studentType != StudentType.None)
  130. {
  131. // 正在执行
  132. throw new Exception("being implemented");
  133. }
  134. IntPtr connId = client.ConnectionId;
  135. Thread thread = new Thread(new ThreadStart(delegate()
  136. {
  137. Student[] students = new Student[2];
  138. students[0] = new Student();
  139. students[0].Id = 1;
  140. students[0].Name = "张三";
  141. students[0].Sex = Sex.Female;
  142. students[1] = new Student();
  143. students[1].Id = 2;
  144. students[1].Name = "李四";
  145. students[1].Sex = Sex.Male;
  146. // 发送数组对象
  147. studentType = StudentType.Array;
  148. if (client.SendBySerializable(students))
  149. {
  150. AddMsg(string.Format("$ ({0}) Send OK --> {1}", connId, "Student[]"));
  151. }
  152. else
  153. {
  154. AddMsg(string.Format("$ ({0}) Send Fail --> {1}", connId, "Student[]"));
  155. }
  156. ////////////////////////////////////////////////////////////////////////////////
  157. // 改变性别
  158. students[0].Sex = Sex.Transsexual;
  159. List<Student> stuList = new List<Student>();
  160. stuList.Add(students[0]);
  161. stuList.Add(students[1]);
  162. // 防止粘包,延迟2秒发送下一组数据
  163. AddMsg(" *** 2 seconds after sending ...");
  164. Thread.Sleep(2000);
  165. // 发送list对象
  166. studentType = StudentType.List;
  167. if (client.SendBySerializable(stuList))
  168. {
  169. AddMsg(string.Format("$ ({0}) Send OK --> {1}", connId, "List<Student>"));
  170. }
  171. else
  172. {
  173. AddMsg(string.Format("$ ({0}) Send Fail --> {1}", connId, "List<Student>"));
  174. }
  175. ////////////////////////////////////////////////////////////////////////////////
  176. // 防止粘包,延迟2秒发送下一组数据
  177. AddMsg(" *** 2 seconds after sending ...");
  178. Thread.Sleep(2000);
  179. // 改变性别
  180. students[0].Sex = Sex.Unknown;
  181. // 发送单个学员信息
  182. studentType = StudentType.Single;
  183. if (client.SendBySerializable(students[0]))
  184. {
  185. AddMsg(string.Format("$ ({0}) Send OK --> {1}", connId, "Student"));
  186. }
  187. else
  188. {
  189. AddMsg(string.Format("$ ({0}) Send Fail --> {1}", connId, "Student"));
  190. }
  191. }));
  192. thread.Start();
  193. }
  194. catch (Exception ex)
  195. {
  196. studentType = StudentType.None;
  197. AddMsg(string.Format("$ Send Fail --> msg ({0})", ex.Message));
  198. }
  199. }
  200. private void lbxMsg_KeyPress(object sender, KeyPressEventArgs e)
  201. {
  202. // 清理listbox
  203. if (e.KeyChar == 'c' || e.KeyChar == 'C')
  204. {
  205. this.lbxMsg.Items.Clear();
  206. }
  207. }
  208. void ConnectUpdateUi()
  209. {
  210. if (this.cbxAsyncConn.Checked == true)
  211. {
  212. SetAppState(AppState.Started);
  213. }
  214. }
  215. private bool SendFileToServer(string filePath)
  216. {
  217. FileInfo fi = new FileInfo(filePath);
  218. if (fi.Length > 4096 * 1024)
  219. {
  220. throw new Exception("被发送文件大小不能超过4096KB。");
  221. }
  222. else if (fi.Length == 0)
  223. {
  224. throw new Exception("文件大小不能为0。");
  225. }
  226. // 头附加数据
  227. MyFileInfo myFile = new MyFileInfo()
  228. {
  229. FilePath = filePath,
  230. FileSize = fi.Length,
  231. };
  232. // 发送文件标记,用来在onrecv里判断是否文件回包
  233. isSendFile = true;
  234. // 不附加尾数据
  235. bool ret = client.SendSmallFile<MyFileInfo, object>(filePath, myFile, null);
  236. if (ret == false)
  237. {
  238. isSendFile = false;
  239. int error = client.SYSGetLastError();
  240. AddMsg(string.Format(" > [SendSmallFile] errorCode:{0}", error));
  241. }
  242. return ret;
  243. }
  244. HandleResult OnPrepareConnect(TcpClient sender, uint socket)
  245. {
  246. return HandleResult.Ok;
  247. }
  248. HandleResult OnConnect(TcpClient sender)
  249. {
  250. // 已连接 到达一次
  251. // 如果是异步联接,更新界面状态
  252. this.Invoke(new ConnectUpdateUiDelegate(ConnectUpdateUi));
  253. AddMsg(string.Format(" > [{0},OnConnect]", sender.ConnectionId));
  254. return HandleResult.Ok;
  255. }
  256. HandleResult OnSend(TcpClient sender, byte[] bytes)
  257. {
  258. // 客户端发数据了
  259. AddMsg(string.Format(" > [{0},OnSend] -> ({1} bytes)", sender.ConnectionId, bytes.Length));
  260. return HandleResult.Ok;
  261. }
  262. HandleResult OnReceive(TcpClient sender, byte[] bytes)
  263. {
  264. // 数据到达了
  265. if (isSendFile == true)
  266. {
  267. // 如果发送了文件,并接收到了返回数据
  268. isSendFile = false;
  269. MyFileInfo myFile = client.BytesToStruct<MyFileInfo>(bytes);
  270. int objSize = Marshal.SizeOf(myFile);
  271. // 因为没有附加尾数据,所以大小可以用length - objSize
  272. byte[] contentBytes = new byte[bytes.Length - objSize];
  273. Array.ConstrainedCopy(bytes, objSize, contentBytes, 0, contentBytes.Length);
  274. string txt = Encoding.Default.GetString(contentBytes);
  275. string msg = string.Empty;
  276. if (txt.Length > 100)
  277. {
  278. msg = txt.Substring(0, 100) + "......";
  279. }
  280. else
  281. {
  282. msg = txt;
  283. }
  284. AddMsg(string.Format(" > [{0},OnReceive] -> FileInfo(Path:\"{1}\",Size:{2})", sender.ConnectionId, myFile.FilePath, myFile.FileSize));
  285. AddMsg(string.Format(" > [{0},OnReceive] -> FileContent(\"{1}\")", sender.ConnectionId, msg));
  286. }
  287. else if (studentType != StudentType.None)
  288. {
  289. switch (studentType)
  290. {
  291. case StudentType.Array:
  292. Student[] students = sender.BytesToObject(bytes) as Student[];
  293. foreach (var stu in students)
  294. {
  295. AddMsg(string.Format(" > [{0},OnReceive] -> Student({1},{2},{3})", sender.ConnectionId, stu.Id, stu.Name, stu.GetSexString()));
  296. }
  297. break;
  298. case StudentType.List:
  299. List<Student> stuList = sender.BytesToObject(bytes) as List<Student>;
  300. foreach (var stu in stuList)
  301. {
  302. AddMsg(string.Format(" > [{0},OnReceive] -> Student({1},{2},{3})", sender.ConnectionId, stu.Id, stu.Name, stu.GetSexString()));
  303. }
  304. break;
  305. case StudentType.Single:
  306. Student student = sender.BytesToObject(bytes) as Student;
  307. AddMsg(string.Format(" > [{0},OnReceive] -> Student({1},{2},{3})", sender.ConnectionId, student.Id, student.Name, student.GetSexString()));
  308. studentType = StudentType.None;
  309. break;
  310. }
  311. }
  312. else
  313. {
  314. AddMsg(string.Format(" > [{0},OnReceive] -> ({1} bytes)", sender.ConnectionId, bytes.Length));
  315. }
  316. return HandleResult.Ok;
  317. }
  318. HandleResult OnClose(TcpClient sender, SocketOperation enOperation, int errorCode)
  319. {
  320. if(errorCode == 0)
  321. // 连接关闭了
  322. AddMsg(string.Format(" > [{0},OnClose]", sender.ConnectionId));
  323. else
  324. // 出错了
  325. AddMsg(string.Format(" > [{0},OnError] -> OP:{1},CODE:{2}", sender.ConnectionId, enOperation, errorCode));
  326. // 通知界面,只处理了连接错误,也没进行是不是连接错误的判断,所以有错误就会设置界面
  327. // 生产环境请自己控制
  328. this.Invoke(new SetAppStateDelegate(SetAppState), AppState.Stoped);
  329. return HandleResult.Ok;
  330. }
  331. /// <summary>
  332. /// 设置程序状态
  333. /// </summary>
  334. /// <param name="state"></param>
  335. void SetAppState(AppState state)
  336. {
  337. appState = state;
  338. this.btnStart.Enabled = (appState == AppState.Stoped);
  339. this.btnStop.Enabled = (appState == AppState.Started);
  340. this.txtIpAddress.Enabled = (appState == AppState.Stoped);
  341. this.txtPort.Enabled = (appState == AppState.Stoped);
  342. this.cbxAsyncConn.Enabled = (appState == AppState.Stoped);
  343. this.btnSend.Enabled = (appState == AppState.Started);
  344. this.btnSendFile.Enabled = (appState == AppState.Started);
  345. this.btnSendSerializableObject.Enabled = (appState == AppState.Started);
  346. }
  347. /// <summary>
  348. /// 往listbox加一条项目
  349. /// </summary>
  350. /// <param name="msg"></param>
  351. void AddMsg(string msg)
  352. {
  353. if (this.lbxMsg.InvokeRequired)
  354. {
  355. // 很帅的调自己
  356. this.lbxMsg.Invoke(AddMsgDelegate, msg);
  357. }
  358. else
  359. {
  360. if (this.lbxMsg.Items.Count > 100)
  361. {
  362. this.lbxMsg.Items.RemoveAt(0);
  363. }
  364. this.lbxMsg.Items.Add(msg);
  365. this.lbxMsg.TopIndex = this.lbxMsg.Items.Count - (int)(this.lbxMsg.Height / this.lbxMsg.ItemHeight);
  366. }
  367. }
  368. private void frmClient_FormClosed(object sender, FormClosedEventArgs e)
  369. {
  370. client.Destroy();
  371. }
  372. private void btnSendFile_Click(object sender, EventArgs e)
  373. {
  374. try
  375. {
  376. OpenFileDialog ofd = new OpenFileDialog();
  377. // ofd.InitialDirectory = "c:\\" ;
  378. //ofd.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
  379. ofd.Filter = "txt files (*.txt)|*.txt";
  380. ofd.RestoreDirectory = true;
  381. ofd.CheckFileExists = true;
  382. ofd.CheckPathExists = true;
  383. if (ofd.ShowDialog() == DialogResult.OK)
  384. {
  385. SendFileToServer(ofd.FileName);
  386. }
  387. }
  388. catch (Exception ex)
  389. {
  390. MessageBox.Show(ex.Message);
  391. }
  392. }
  393. }
  394. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  395. public struct MyFileInfo
  396. {
  397. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
  398. public string FilePath;
  399. public long FileSize;
  400. }
  401. /// <summary>
  402. /// 学员性别
  403. /// </summary>
  404. public enum Sex
  405. {
  406. /// <summary>
  407. /// 男
  408. /// </summary>
  409. Male = 1,
  410. /// <summary>
  411. /// 女
  412. /// </summary>
  413. Female = 2,
  414. /// <summary>
  415. /// 变性人
  416. /// </summary>
  417. Transsexual = 3,
  418. /// <summary>
  419. /// 未知
  420. /// </summary>
  421. Unknown = 4,
  422. }
  423. /// <summary>
  424. /// 学员类
  425. /// </summary>
  426. [Serializable]
  427. public class Student
  428. {
  429. /// <summary>
  430. /// 编号
  431. /// </summary>
  432. public int Id { get; set; }
  433. /// <summary>
  434. /// 姓名
  435. /// </summary>
  436. public string Name { get; set; }
  437. /// <summary>
  438. /// 性别
  439. /// </summary>
  440. public Sex Sex { get; set; }
  441. public string GetSexString()
  442. {
  443. string sex = string.Empty;
  444. switch (Sex)
  445. {
  446. case Sex.Male:
  447. sex = "男";
  448. break;
  449. case Sex.Female:
  450. sex = "女";
  451. break;
  452. case Sex.Transsexual:
  453. sex = "变性人";
  454. break;
  455. default:
  456. sex = "未知";
  457. break;
  458. }
  459. return sex;
  460. }
  461. }
  462. }