学习 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.



 

原创粉丝点击