.net平台下C#socket通信(转)

上篇.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
    }

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方法中使用死循环开启并保持监听。

时间: 2024-10-17 23:39:51

.net平台下C#socket通信(转)的相关文章

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

本文主要讲述: 1.正常通信中握手建立 2.一对多的通信 3.发送接收数据格式转换 4.资源释放 5.开启并保持服务监听 1.握手建立正常的通信通道 项目需要通信的双方(假设是一个上位机.一个下位机)之间需要建立一个稳定的通道,以便进行通信.本项目中具体操作是:上位机作为服务器,下位机作为客户端,同时制定通信协议.上位机首先打开监听等待建立通道,下位机主动连接上位机后发送连接成功的信息到上位机,上位机根据通信协议发送数据到下位机,此时通道已经建立.但为了保险起见(同时遵循三次握手),客户端再次发

.net平台下C#socket通信(上)

在开始介绍socket前先补充补充基础知识,在此基础上理解网络通信才会顺理成章,当然有基础的可以跳过去了.都是废话,进入正题. TCP/IP:Transmission Control Protocol/Internet Protocol,传输控制协议/因特网互联协议,又名网络通讯协议.简单来说:TCP控制传输数据,负责发现传输的问题,一旦有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地,而IP是负责给因特网中的每一台电脑定义一个地址,以便传输.从协议分层模型方面来讲:TCP/I

移动平台下的Socket几个问题

在页游时代,使用Flash ActionScript 3.0进行开发,as3提供比较简单和健全的socket API.到了手游时代,基于tcp的socket编程遇到了一些棘手的问题.通常情况下手游都要支持至少二大主流平台:Android.IOS,二者共存,暂时没有迹象表现哪一方会没落. 页游跑在浏览器里,所有的连接成功.失败等操作,都可以通过addEventListener进行监听,很方便,一般也不存在频繁掉线的情况.而手游,因为手机的便携性决定了它的移动性,既然是可移动的那就会一定会面临网络不

iOS开发之Socket通信实战--Request请求模块

实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncSocket来进行长连接连接和传输数据,该第三方地址:https://github.com/robbiehanson/CocoaAsyncSocket,读者可以自行google或者baidu搜索这个库的用法,网上有很多资料,而且用法不难. 在一些对Socket通信使用需求不是很高的应用中,比如需要多个i

iOS开发之Socket通信实战--Request请求数据包编码模块

实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncSocket来进行长连接连接和传输数据,该第三方地 址:https://github.com/robbiehanson/CocoaAsyncSocket,读者可以自行google或者baidu搜索 这个库的用法,网上有很多资料,而且用法不难. 在一些对Socket通信使用需求不是很高的应用中,比如需要

MAC平台下Xcode配置使用OpenCV的具体方法 (2016最新)

1.序言: 1.1 背景 本人小白一枚,不过因为最近在从事机器视觉方面的工作,所以接触到OpenCV. 因为工作需求,本人要在MAC端使用OpenCV实现一些视觉功能,配置环境成了最大的阻碍,网上查了很多相关资料和博客,都因为版本环境问题屡试屡败,不过经历重重尝试,笔者最终还是配置成功并运行了自己的源码.当然成功的关键还是因为笔者站在了巨人的肩膀上,借鉴了很多网上的教程,为了不误导大家配置的过程,参考文章的地址统一放在文章里,望各位大大看见之后能够理解,废话不说进入正题. 1.2 环境说明 如果

TCP及socket通信原理

一.网络互联模型 因特网在刚面世时,只有同一制造商生产的计算机才能彼此通信,制定网络互联模型的目的就是为异种的计算机互连提供一个共同的基础和标准框架,并为保持相关标准的一致性和兼容性提供共同的参考. 互联参考模型: OSI七层模型(Open System Interconnect):应用层.表示层.会话层.传输层.网络层.数据链路层.物理层 DoD四层模型:是OSI七层模型的浓缩版,包括 进程/应用层.主机到主机层.因特网层.网络接入层 以上两种模型是层次型的,分层模型的优点主要在于: ①将网络

java socket通信-传输文件图片--传输图片

ClientTcpSend.java   客户端发送类 package com.yjf.test; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.net.InetSocketAddress; import java.net.Socket; public class ClientTcpSend { public static String clien

c# socket通信较完善方案

c#的socket通信应用.文件较多.附件为工程.  core AbstractBytesWorker.cs    字节工作器(基类),用于用于同一不同功能的字节工作器 BinaryHand.cs  2进制处理器.  ThDispose.cs 处理回收相关 crc  entity ThPersonInfo.cs manager ThSocketManager.cs  ThSocketManagerBusiness.cs 所有的业务 request RequestCode.cs  请求码 ThPr