国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

.net平臺下C#socket通信(中)

2019-11-14 16:21:36
字體:
來源:轉載
供稿:網友

   上篇.net平臺下C#socket通信(上)介紹了socket通信的基本原理及最基本的通信方式。本文在此基礎上就socket通信時經常遇到的問題做一個簡單總結,都是項目中的一些小問題,拿來此處便于下次使用,同時對在使用socket時出現些許問題的同仁們多一個粗淺建議。不足之處請提出,謝謝。

本文主要講述:

1、正常通信中握手建立

2、一對多的通信

3、發送接收數據格式轉換

4、資源釋放

5、開啟并保持服務監聽

 

1、握手建立正常的通信通道

  項目需要通信的雙方(假設是一個上位機、一個下位機)之間需要建立一個穩定的通道,以便進行通信。本項目中具體操作是:上位機作為服務器,下位機作為客戶端,同時制定通信協議。上位機首先打開監聽等待建立通道,下位機主動連接上位機后發送連接成功的信息到上位機,上位機根據通信協議發送數據到下位機,此時通道已經建立。但為了保險起見(同時遵循三次握手),客戶端再次發送數據到上位機告知通道建立完畢。

 

2、一對多通信

  項目需求是一個上位機多個下位機,這就確定了上位機做為服務器端,下位機作為客戶端主動連接服務器。一對一通信時只有一個socket通道,因此無論是上位機還是下位機在發送和接收數據的時候都不會存在數據亂發亂收的情況。一對多意味著上位機和下位機會建立起多個通道,因此在發送數據時需要記錄哪一個下位機處于哪個socket通道中,以便進行邏輯處理。本文處理一對多通信的過程是:

1)首先建立一個對話類session

public class Session    {        public Socket ClientSocket { get; set; }//客戶端的socket        public string ip;//客戶端的ip        public Session(Socket clientSocket)        {            this.ClientSocket = clientSocket;            this.IP = GetIPString();        }        public string GetIPString()        {            string result = ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString();            return result;        }    }

 

2)在服務端socket監聽時:

IPEndPoint loaclEndPoint = new IPEndPoint(IPAddress.Any, Port);            SocketLister = new Socket(AddressFamily.InterNetwork, SocketType.Stream, PRotocolType.Tcp);            SocketLister.Bind(loaclEndPoint);            try            {                SocketLister.Listen(MaxConnection);                while (IsRunning)                {                    ClientSocket = SocketLister.Accept();                    //保存socket                    Session newSession = new Session(ClientSocket);                    lock (sessionLock)                    {                        sessionTable.Add(newSession.IP, newSession);                    }                    SocketConnection socketConnection = new SocketConnection(ClientSocket);                    socketConnection.ReceiveDatagram();//接收數據                }            }            catch (SocketException ex)            {            }



//聲明

public Hashtable sessionTable = new Hashtable ();//包括客戶端會話

private object sessionLock = new object();

 

為了便于理解,把整個服務端socket的建立都寫在上面。

 

3)發送數據到不同的客戶端

Hashtable ht = serverSocket.sessionTable;            foreach (Session session in ht.Values)            {                if (session.IP == "127.0.0.1")//example                {                    SocketConnection socketConnection = new SocketConnection(session.ClientSocket);                    string str = "C300010002D2";                    byte[] sendBytes = StrToHexByte(str);                    socketConnection.Send(sendBytes);                }                           }

SocketConnection類已經被使用多次,寫在下面:

 

public class SocketConnection:IDisposable     {        public ServerSocket Server { get; set; }        public Byte[] MsgBuffer = null;        private int totalLength = 0;        public int CurrentBufferLength;        private Socket _ClientSocket = null;        public Socket ClientSock        {            get{ return this._ClientSocket; }        }        public SocketConnectionType Type { get; private set; }        #region Constructor        public SocketConnection(ServerSocket server, Socket sock)        {            this.Server = server;            this._ClientSocket = sock;            this.Type = SocketConnectionType.Server;        }        public SocketConnection(Socket sock)        {            this._ClientSocket = sock;            this.Type = SocketConnectionType.Client;        }        #endregion         #region Events        public SocketConnectionDelegate OnConnect = null;//是否連接        public SocketConnectionDelegate OnLostConnect = null;//中斷連接        public ReceiveDataDelegate OnReceiveData = null;//接收數據        #endregion         #region Connect        public void Connect(IPAddress ip, int port)        {            this.ClientSock.BeginConnect(ip, port, ConnectCallback, this.ClientSock);        }        private void ConnectCallback(IAsyncResult ar)        {            try            {                Socket handler = (Socket)ar.AsyncState;                handler.EndConnect(ar);                if (OnConnect != null)                {                    OnConnect(this);                }                ReceiveDatagram();            }            catch (SocketException ex)            {            }        }        #endregion        #region Send        public void Send(string data)        {            Send(System.Text.Encoding.UTF8.GetBytes(data));        }        public void Send(byte[] byteData)        {            try            {                int length = byteData.Length;                byte[] head = BitConverter.GetBytes(length);                byte[] data = new byte[head.Length + byteData.Length];                Array.Copy(head, data, head.Length);                Array.Copy(byteData, 0, data, head.Length, byteData.Length);                this.ClientSock.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), this.ClientSock);            }            catch (SocketException ex)            {            }        }        private void SendCallback(IAsyncResult ar)        {            try            {                Socket handler = (Socket)ar.AsyncState;                handler.EndSend(ar);            }            catch (SocketException ex)            {            }        }        #endregion        #region ReceiveDatagram             public void ReceiveDatagram()        {            SocketStateObject state = new SocketStateObject();            state.workSocket = _ClientSocket;            _ClientSocket.BeginReceive(state.buffer, 0, SocketStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);        }        private void ReceiveCallback(IAsyncResult ar)        {            SocketStateObject state = (SocketStateObject)ar.AsyncState;            Socket handler = state.workSocket;            if (handler.Connected)            {                try                {                    state.bytesRead = handler.EndReceive(ar);                    if (state.bytesRead > 0)                    {                        OnDataRecivedCallback(state.buffer, state.bytesRead);                        Array.Clear(state.buffer, 0, state.buffer.Length);                        state.bytesRead = 0;                        handler.BeginReceive(state.buffer, 0, SocketStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);                    }                    else                    {                        //if (OnDisconnect != null)                        //{                        //    OnDisconnect(this);                        //}                        Dispose();                    }                }                catch (SocketException ex)                { }            }        }        private void ReceiveCallBack00(IAsyncResult ar)        {            try            {                int REnd = _ClientSocket.EndReceive(ar);                if (REnd > 0)                {                    OnDataRecivedCallback(MsgBuffer, REnd );                    Array.Clear(MsgBuffer, 0, MsgBuffer.Length);                    REnd = 0;                    _ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallBack00), null);                }                else                {                    if (OnLostConnect != null)                    {                        OnLostConnect(this);                    }                    Dispose();                }            }            catch (Exception ex)            {            }        }        private void OnDataRecivedCallback(byte[] data, int length)        {            if (length > 0)            {                if (this.MsgBuffer == null)                {                    byte[] bytelength = new byte[4];                    Array.Copy(data, bytelength, 4);                    this.totalLength = BitConverter.ToInt32(bytelength, 0);                    this.MsgBuffer = new byte[this.totalLength];                    this.CurrentBufferLength = 0;                }                if (this.totalLength > 0)                {                    int offset = 0;                    if (CurrentBufferLength == 0)                    {                        offset = 4;                    }                    if (length + this.CurrentBufferLength >= this.totalLength + offset)                    {                        int len = this.totalLength - CurrentBufferLength;                        Array.Copy(data, offset, this.MsgBuffer, this.CurrentBufferLength, len);                        byte[] tmp = this.MsgBuffer.Clone() as byte[];                        OnReceiveData(new MessageData(this, tmp));                        this.MsgBuffer = null;                        if (length - len - offset > 0)                        {                            tmp = new byte[length - len - offset];                            Array.Copy(data, offset + len, tmp, 0, tmp.Length);                            OnDataRecivedCallback(tmp, tmp.Length);                        }                    }                    else                    {                        Array.Copy(data, offset, this.MsgBuffer, this.CurrentBufferLength, length - offset);                        this.CurrentBufferLength += length - offset;                    }                }                else                {                }            }        }        public void Dispose()        {            try            {                this.ClientSock.Shutdown(SocketShutdown.Both);                this.ClientSock.Close();                this.Server = null;                //if (OnLostConnect != null)                //{                //    OnLostConnect(this);                //}            }            catch            {            }        }        #endregion    }
View Code

 

 

 

3、處理需要發送和接收到的數據

  項目需要是上位機獲取數據進行邏輯處理,然后通過tcp/ip協議發送給下位機,下位機在接收到數據的同時發送確認信息到上位機。項目過程中,上位機在開發過程中需要調試其發送數據、接收數據是否成功,此處借助于USR-  TCP232小工具。但是涉及到一個問題,下位機發送和接收都是byte字節數組,那么開發的上位機應該如何發送和接收數據?在.net平臺下C#socket通信(上),有服務器端的發送和接收函數,發送數據時將要發送的字符串轉換為byte數組,接收時再將字節數組轉換為16進制字符串。如下:

//字節數組轉換為16進制字符串        public string ByteToHexStr(byte[] bytes)        {            string str = "";            if (bytes != null)            {                for (int i = 0; i < bytes.Length; i++)                {                    str += bytes[i].ToString("X2");                }            }            return str;        }        //字符串轉換為16進制byte數組        public byte[] StrToHexByte(string data)        {            data = data.Replace(" ", "");            if ((data.Length % 2) != 0)            {                data += " ";            }            byte[] bytes = new byte[data.Length / 2];            for (int i = 0; i < bytes.Length; i++)            {                 bytes[i] = Convert .ToByte (data.Substring (i * 2,2),16);            }            return bytes;        }

 

4、資源釋放

  開發項目使用平臺是.net,工具vs2010,語言是C#,因為.net有垃圾回收機制,因此在實際開發中產生的托管資源都是系統自動釋放完成。在做本項目時采用winform進行開發的,在此過程中發現一個問題:在關閉Form窗體是運行的系統并沒有完全關閉。查找原因,應該是有資源沒有被釋放。而socket套接字產生的資源恰好是非托管資源,此現象表明系統中有socket資源沒有被完全釋放掉。因此寫了一個資源釋放函數:

public void Dispose()        {            try            {                this.ClientSocket.Shutdown(SocketShutdown.Both);                this.ClientSocket.Dispose();                this.ClientSocket.Close();                this.ClientSocket = null;            }            catch            {            }        }

上述函數的功能就是釋放socket所產生的資源,調用后發現還是存在此問題,幾經調試發現雖然把產生socket通道的監聽客戶端資源釋放完畢,服務器端的serversocket并沒有被釋放,于是有了下一個函數:

public void CloseSocket()        {            if (serverSocket != null)            {                serverSocket.SocketLister.Dispose();                serverSocket.SocketLister = null;                serverSocket.Dispose();//調用的上一個函數                serverSocket = null;            }        }

在上述函數完成后,套接字socket所產生的資源確實被釋放完畢,系統在form關閉后能真正關閉。到此資源好像已經被釋放掉,但緊接著新的問題產生了:

在什么時候什么地方調用釋放資源的函數?

個人簡單看法:

1)系統中止時調用

2)socket通道中斷時調用

 

補充:

5、開啟并保持服務監聽

在socket通信中,服務端的socket監聽其實是需要一直打開并且保持的,只有這樣才能隨時監聽連接的客戶端。項目中示例:

private void button1_Click(object sender, RoutedEventArgs e)        {            Thread thread = new Thread(new ThreadStart(StartServer));            thread.Start();        }public void StartServer()        {            int port = Convert.ToInt32(GetText(this.tbPort));            string ipStr = GetText (this.tbServerIPStr);            if (serverSocket == null)            {                serverSocket = new ServerSocket(port);                serverSocket.Start(ipStr);//            }            else            {                MessageBox.Show("監聽已開啟");            }                 }public void Start(string ipStr)        {            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, Port);            //IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(ipStr), Port);            SocketLister = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            SocketLister.Bind(localEndPoint);            try            {                SocketLister.Listen(MaxConnection);                while (IsRunning)                {                    ClientSocket = SocketLister.Accept();                    //保存socket                    Session newSession = new Session(ClientSocket);                    lock (sessionLock)                    {                        sessionTable.Add(newSession.IP, newSession);                    }                    SocketConnection socketConnection = new SocketConnection(ClientSocket);                    socketConnection.ReceiveDatagram();                }            }            catch (SocketException ex)            {            }        }

解釋:點擊按鈕開啟新的線程thread,執行方法StartServer,StartServer調用方法Start,Start方法中使用死循環開啟并保持監聽。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 汾西县| 涡阳县| 郑州市| 滨州市| 达州市| 靖江市| 太谷县| 霍山县| 娄底市| 武川县| 滁州市| 清水县| 中宁县| 罗甸县| 肇庆市| 慈利县| 沅江市| 丹凤县| 颍上县| 汝州市| 南涧| 商丘市| 武邑县| 泰安市| 驻马店市| 陵川县| 遵化市| 扬中市| 黑龙江省| 铅山县| 咸丰县| 沂源县| 乌鲁木齐市| 武强县| 会宁县| 巴林左旗| 镇雄县| 玉田县| 西畴县| 青州市| 繁峙县|