学习 C# Socket 编程(一):Client 端的实现
来源:互联网 发布:网络实名制 人民日报 编辑:程序博客网 时间:2024/06/02 08:01
这几天闲来无事,研究了一下Socket编程。小妹刚做这行不就,什么都得从头学起,希望各位高手多多指点,指出错误与不足,小妹铭感于心。
在网上浏览一些基础的Socket编程的例子之后,很多人建议从socket这个类开始学起,因为这个类比较底层,可以通过这个类更深入理解socket. 于是,我根据自己浅显的理解,写出了如下一个小程序。这个程序的基本用途是和我们公司的一个产品级的Server交互,使用TCP/IP协议,基于Winform的多线程应用。
这个程序的界面如图所示。输入IP, 端口号以及数据的读取路径后,用户每点击一次Connect按钮,程序就开启一个子线程,然后在这个子线程里,和Server建立一个TCP连接,不断地发送和接受数据。
下面给出button的click函数:
private void btnConnect_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(tbxIP.Text)) { MessageBox.Show("Please input Key Server IP !"); return; } if (string.IsNullOrEmpty(tbxPort.Text)) { MessageBox.Show("Please input Key Server port number !"); return; } if (string.IsNullOrEmpty(tbxDataPath.Text)) { MessageBox.Show("Please select data path !"); return; } string IP = tbxIP.Text.Trim(); int port = int.Parse(tbxPort.Text.Trim()); string dataPath = tbxDataPath.Text.Trim(); // Class Client encapsulate the necessary data (port, ip and form1) to pass these data to client.ConnectToServer. Client client = new Client(IP, port, dataPath, this); Thread thread = new Thread(new ThreadStart(client.ConnectToServer)); thread.Start(); }
这个Client类里封装了一个TCP连接必要的数据(IP, 端口号,发送数据文件),和一个线程的执行方法 (ConnectToServer)。下面给出这个方法:
public void ConnectToServer() { bool bConnected = true; Socket sockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); DataProcess dataProcess = new DataProcess("..\\..\\TestData\\Test"); sockClient.Connect(IPAddress.Parse(ip), port); while (bConnected) { try { foreach (string strCommand in dataProcess.MSP) { byte[] bytReq = Encoding.UTF8.GetBytes(strCommand); int sendCnt = sockClient.Send(bytReq); if (sendCnt <= 0) { bConnected = false; sockClient.Close(); } else { byte[] bytRec = new byte[1024]; int recCnt = sockClient.Receive(bytRec); if (recCnt <= 0) { bConnected = false; sockClient.Close(); } else { string strRec = Encoding.UTF8.GetString(bytRec); UpdateListView(sockClient, strRec); // Be locked ? Thread.Sleep(1000); } } } } catch (SocketException e) { bConnected = false; sockClient.Close(); MessageBox.Show(e.Message); } catch (ObjectDisposedException e){ bConnected = false; sockClient.Close(); MessageBox.Show(e.Message); } } }
在这个方法里,首先创建一个局部变量socket, 然后和Server建立连接,然后进入一个死循环,不断地给Server端发送和接收数据,这里使用同步方法。其中DataProcess类负责读取发送数据文件,UpdateListView 负责在Form上显示Server端信息以及接收到的数据。
这就是我的第一版程序代码,当晚便迫不及待的进行了测试,结果第二天早上发现:CPU占用100%......
在咨询了一些同事并在网上查询之后,我发现了三个可疑之处:
(1)程序频繁的刷新界面,耗费CPU资源;
(2)程序中包含死循环,而每两次循环之间没有时间间隔,同事建议让CPU休息100ms.
(3)每一次循环都要new一个1024B的数组,这个局部变量,增加了CLR分配和回收内存的负担。
首先我把刷新界面的代码(UpdateListView) 注释掉,然后重新测试,结果照旧。于是有修改了代码:给每两次循环之间加了100ms延时,并且在方法顶部new了一个1024B的数组,反复使用。这次问题终于解决了!!! 修改后的代码如下:
public void ConnectToServer() { bool bConnected = true; byte[] bytReq = new byte[4096]; // re-use the buffer for requests and responses. byte[] bytRec = new byte[4096]; int conn = 0; string threadID = Thread.CurrentThread.ManagedThreadId.ToString(); Trace.WriteLine("Thread " + threadID + " start !"); Socket sockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sockClient.Connect(IPAddress.Parse(ip), port); // try/catch ???if (sockClient.Connected) { while (true) { if (!bConnected) { break; } foreach (string strCommand in data.MSPRequest) { if (!bConnected) { break; } bytReq = GeneralLibrary.HexString2ByteArray(strCommand); try { int sendCnt = sockClient.Send(bytReq); if (sendCnt <= 0) { bConnected = false; Trace.WriteLine(threadID + " : The sendCnt is less than or equals to 0."); sockClient.Shutdown(SocketShutdown.Both); sockClient.Close(); } else { int recCnt = sockClient.Receive(bytRec); if (recCnt <= 0) { bConnected = false; Trace.WriteLine(threadID + " : The recCnt is less than or equals to 0."); sockClient.Shutdown(SocketShutdown.Both); sockClient.Close(); } else { StringBuilder sb = new StringBuilder(); for (int i = 0; i < recCnt; i++) { string tmp = string.Format("{0:X2}", bytRec[i]); sb.Append(tmp); } data.MSPResponse.Add(sb.ToString().Trim()); if (data.MSPResponse.Count == LOGCOUNT) { lock (synobj) { Log.SaveToLog(data.MSPResponse); } data.MSPResponse.Clear(); } //UpdateListView(sockClient, sb.ToString()); // Be locked ? } } } catch (SocketException e) { bConnected = false; Trace.WriteLine(threadID + " : SocketException !"); Trace.WriteLine(e.ErrorCode + " : " + e.Message); sockClient.Shutdown(SocketShutdown.Both); sockClient.Close(); } catch (ObjectDisposedException e) { bConnected = false; Trace.WriteLine(threadID + " : ObjectDisposedException !"); Trace.WriteLine(e.Message); sockClient.Shutdown(SocketShutdown.Both); sockClient.Close(); } catch (Exception e) { bConnected = false; Trace.WriteLine(threadID + " : Exception !"); Trace.WriteLine(e.Message); sockClient.Shutdown(SocketShutdown.Both); sockClient.Close(); } } // end of foreach Thread.Sleep(TIMEOUT); // Sleep 0.1s to avoid CPU usage 100%. } //end of while Thread.Sleep(TIMEOUT); // Sleep 0.1s to avoid CPU usage 100%. } else // if(sockClient.Connected) { Trace.WriteLine("The connection cannot be set up!"); sockClient.Close(); } MessageBox.Show("Done!"); }
这下程序总算运行比较稳定了,但是有时候还会遇到一些莫名其妙的错误码。同时还遇到工作中的另一个问题:其他部门的同事抱怨从Sever端接收的数据不全,于是我决定用这个小程序测试一下Server.
- 学习 C# Socket 编程(一):Client 端的实现
- Python学习笔记之socket编程——简单聊天器的雏形(client端)
- C++ Socket编程 基础一:简单的server 和client
- Socket编程(一)简单Client与Server的单向通信和双向通信
- Java Socket编程系列(一)开发一次性会话的Server和Client
- C# client端socket連線實作經驗
- 【Socket编程】使用C++实现Server端和Client端
- [MFC学习笔记]--SOCKET编程只client端\UDPsocket通信
- java socket 编程(Client)
- Unix网络编程学习日记(一):半双工非阻塞socket客户端的实现
- C# Socket多线程编程(一)
- C# Socket多线程编程(一)
- 异步Socket通信编程的C#实现(1)
- 异步Socket通信编程的C#实现(2)
- Winsock学习2:用winsock实现socket通讯的client端。
- Visual C#托管Socket的实现方法(一) (2)
- Visual C#托管Socket的实现方法(一) (1)
- Visual C#托管Socket的实现方法(一) (3)
- Sql Server数据库性能优化需注意哪些地方
- SQL 多行更新
- 逆向反汇编代码推算C++的局部变量
- 学习笔记_java CAS单点登录(SSO)
- win7权限问题
- 学习 C# Socket 编程(一):Client 端的实现
- TextView加边框
- mongodb 锁
- 黑马程序员--java内部类与异常
- public,protected,private的设计初衷
- mongodb date type
- 折半插入排序
- java将数据写入到txt文件中(txt有固定的格式)
- mongodb replica sets reconfig and conver a Secondary to an Arbiter