Socket异步,多设备接收和发送时,导致某一些设备无法接收到数据

JZ_7975 2015-01-20 10:28:05
业务场景:
1、socket 异步写的采集,主要负责接收、发送和逻辑处理
2、设备每次发送固定长度的字节数据来传输照片(0X76),采集在接收到报文后,发送一个确认报文(0X77) ,
设备再继续发送下一帧的报文
3、A设备在传报文过程中, 采集接收到命令,发送传输照片的命令给B设备,这个时候A设备就接收不到 采集发送
的确认报文了(看了日志,实际上是采集没有发送确认报文)


异步的代码主要是参考MSDN中的例子,觉得应该在接收和处理那里加上线程,否则设备数量一多(大概有3000)
必然是出问题的,下面为主要逻辑代码,麻烦给帮忙看看

/// <summary>
/// socket服务,保持一个Socket
/// </summary>
public class SocketServer
{
#region 自定义变量
//线程的单利模式
public static ManualResetEvent AllDone = new ManualResetEvent(false);
//存储客户端IP和Message0x75报文
public static Hashtable HtMessaeg0X75 = new Hashtable();
//存储放映机信息
public static Hashtable HtFyjInfo = new Hashtable();
public static Socket Handler = null;
public static byte[] FrameData;//帧数据,用于粘包处理
private static int frameMessageLength = 0;
private static Socket listener;

#endregion

//----------------------线程----------------------
#region 监听
public static void StartListening()
{
try
{
//信息显示与记录
const string startInfo = "数据采集线程已启动";
MainForm.AppendRunInfo(startInfo);//显示在窗体
LogInfo.Instance.SetMessage("INFO", startInfo);//日志记录

//采集配置信息
string ip = null;
int port = 0;
var serverInfo = SystemConfig.GetServerInfo();
if (string.IsNullOrEmpty(serverInfo))
{
var logInfo = "采集IP为空";
MainForm.AppendRunInfo(logInfo);
LogInfo.Instance.SetMessage("INFO", logInfo);
return;
}
else
{
var info = serverInfo.Split(':');
if (info.Length != 2)
{
var logInfo = "采集IP设置错误:" + serverInfo;
MainForm.AppendRunInfo(logInfo);
LogInfo.Instance.SetMessage("INFO", logInfo);
return;
}
else
{
ip = info[0];
port = Convert.ToInt16(info[1]);
}
}
var ipAddress = IPAddress.Parse(ip);

if (port > 65535)
{
var portInfo = "端口" + port + ",超过最大范围65535";
//信息显示与记录
MainForm.AppendRunInfo(portInfo);
LogInfo.Instance.SetMessage("INFO", portInfo);
return;
}
var localEndPoint = new IPEndPoint(ipAddress, port);
var connectionInfo = "数据采集的IP:" + ipAddress + ",端口:" + port;
MainForm.AppendRunInfo(connectionInfo);
LogInfo.Instance.SetMessage("INFO", connectionInfo);
listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
//绑定
listener.Bind(localEndPoint);
listener.Listen(100);

var thread = new Thread(new ThreadStart(Target));
thread.Start();
}
catch (Exception ex)
{
LogError.Instance.SetMessage("ERROR", ex.ToString());
}
}
#endregion



private static void Target()
{
LogInfo.Instance.SetMessage("INFO", "启动线程来接收Target");
while (true)
{
//设置状态为无信号
AllDone.Reset();
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
// 阻塞线程
AllDone.WaitOne();
}
}

#region 监听线程
/// <summary>
/// 监听线程
/// </summary>
public void ThreadSocketListening()
{
try
{
//监听线程
var tsListening = new ThreadStart(StartListening);
var tListening = new Thread(tsListening)
{
IsBackground = true//后台线程
};
tListening.Start();
LogInfo.Instance.SetMessage("INFO", "监听线程启动");
}
catch (Exception ex)
{
LogError.Instance.SetMessage("ERROR", ex.ToString());
}
}
#endregion
//----------------------异步相关----------------------
#region 异步连接的回调函数
public static void AcceptCallback(IAsyncResult ar)
{
try
{
AllDone.Set(); //设置状态为有信号
var listener = (Socket)ar.AsyncState;


var handler = listener.EndAccept(ar);
//客户端IP
var clientIp = IPAddress.Parse(((IPEndPoint)handler.RemoteEndPoint).Address.ToString()) + ":" + ((IPEndPoint)handler.RemoteEndPoint).Port.ToString(CultureInfo.InvariantCulture);
//信息显示
var clientRunInfo = "放映机:" + clientIp + "与数据采集已连接";
LogInfo.Instance.SetMessage("INFO", clientRunInfo);
MainForm.AppendRunInfo(clientRunInfo);

//将和采集建立连接的放映机存储至哈希表中
var mss = new ModuleSocketInfo()
{
handleSocket = handler,
connectionState = "0"//未连接,仅仅是建立socket连接,而非和放映机建立连接
};
mss.CumulativeSerialNumber();
HtFyjInfo.Add(clientIp, mss);

//在与模块建立连接后,发送0x00报文
var message = SocketActivateCertification.Message0x00();
handler.BeginSend(message, 0, message.Length, 0,
new AsyncCallback(SendCallback), handler);
//输出记录
var sendMessgae = CommonTools.BytesToString(message);
LogInfo.Instance.SetMessage("INFO", "采集发送0x00:" + sendMessgae + "给放映机IP:" + clientIp);
PrintHtFyjInfo();
var state = new StateObject
{
workSocket = handler
};
//异步接收数据
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
catch (Exception ex)
{
LogError.Instance.SetMessage("ERROR", ex.ToString());
}
}
#endregion
...全文
645 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
生财 2015-01-27
  • 打赏
  • 举报
回复
楼主的服务端代码很有问题.. 异步操作应该为每一个TCP联接创建一个独立的接收数据对像用来拆包发送等. 发送数据需要用对应的Socet对像发送, 关于异步发送可以参考如下:http://blog.csdn.net/daonidedie/article/details/41345673
   /// <summary>
    /// 用于辅助异步发送Socket的帮肋类
    /// </summary>
    public class AsyncSocketSendHelper
    {

        #region 客户端联接池
        /// <summary>
        /// 接收SAEA池集合
        /// </summary>
        private ConcurrentStack<SocketAsyncEventArgs> ReceiveSocketArgsPool = new ConcurrentStack<SocketAsyncEventArgs>();

        /// <summary>
        /// 初始化接收socketargs对象池
        /// </summary>
        protected void InitReceiveSocketArgsPool()
        {
            for (int ndx = 0; ndx < 3000 ; ndx++)
            {
                ReceiveSocketArgsPool.Push(CreateReceiveSocketArgs());
            }
        }

        /// <summary>
        /// 创建一个接收SAEA对象,设置最大接收字节数
        /// </summary>
        /// <returns></returns>
        protected virtual SocketAsyncEventArgs CreateReceiveSocketArgs()
        {
            SocketAsyncEventArgs e = new SocketAsyncEventArgs();
            e.Completed += IO_SendComleted;
            return e;
        }




        /// <summary>
        /// 租赁一个接收SAEA对象
        /// </summary>
        /// <returns></returns>
        protected virtual SocketAsyncEventArgs RentReveiveSocketArgs()
        {
            SocketAsyncEventArgs e;

            return ReceiveSocketArgsPool.TryPop(out e) ? e : CreateReceiveSocketArgs();
        }

        /// <summary>
        /// 归还一个接收SAEA参数
        /// </summary>
        /// <param name="e">还池</param>
        protected virtual void GivebackReceiveSocketArgs(SocketAsyncEventArgs e)
        {
            if (e != null)
            {
                e.UserToken = null;
                ReceiveSocketArgsPool.Push(e);
            }
        }

        #endregion

        /// <summary>
        /// 发送数据服务
        /// </summary>
        /// <param name="socket">用于发送的Socket对像</param>
        /// <param name="buff">需要发送的数据</param>
        /// <param name="callBack">回调函数参数为:发送结果,目标节点,发送数据</param>
        /// <param name="userToken">用户数据 </param>
        /// <returns></returns>
        public virtual void SendToAsync(Socket socket, byte[] buff, Action<AsyncSendResult> callBack = null, object userToken = null)
        {
            if (socket == null)
            {
                throw new NullReferenceException();
            }

            if (buff == null)
            {
                throw new NullReferenceException();
            }

            SocketAsyncEventArgs e = RentReveiveSocketArgs();
            //设置发送缓冲区
            e.SetBuffer(buff, 0, buff.Length);
            try
            {
                //用户标识
                var token = new AsyncSendResult
                {
                    Result = false,
                    RemoteEndPoint = socket.RemoteEndPoint.ToString(),
                    SendTime = DateTime.Now,
                    SendData = buff,
                    ResultTime = DateTime.Now,
                    CallBakc = callBack,
                    UserToKen = userToken,
                };
                e.UserToken = token;
                //发送数据
                if (!socket.SendAsync(e))
                {
                    IO_SendComleted(socket, e);
                }
            }
            catch (SocketException)
            {
                //还池
                GivebackReceiveSocketArgs(e);
            }
            catch (ObjectDisposedException)
            {
                //还池
                GivebackReceiveSocketArgs(e);
            }
        }

        /// <summary>
        /// 发送数据后的完成功能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void IO_SendComleted(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                if (e.UserToken != null)
                {
                    AsyncSendResult token = e.UserToken as AsyncSendResult;
                    if (token != null)
                    {
                        //设置结果时间
                        token.ResultTime = DateTime.Now;

                        //发送数据OK
                        if (e.SocketError == SocketError.Success)
                        {
                            token.Result = true;
                            if (token.CallBakc != null)
                            {
                                token.CallBakc(token);
                            }

                        }
                        else
                        {
                            //发送数据失败
                            token.Result = false;
                            if (token.CallBakc != null)
                            {
                                token.CallBakc(token);
                            }
                        }
                    }
                }
            }
            finally
            {
                //还池
                GivebackReceiveSocketArgs(e);
            }
        }






    }

    /// <summary>
    /// 异步发送结果
    /// </summary>
    public class AsyncSendResult
    {
        /// <summary>
        /// 结果
        /// </summary>
        public bool Result { get; set; }

        /// <summary>
        /// 目标节点
        /// </summary>
        public string RemoteEndPoint { get; set; }

        /// <summary>
        /// 发送数据
        /// </summary>
        public byte[] SendData { get; set; }

        /// <summary>
        /// 发送时间
        /// </summary>
        public DateTime SendTime { get; set; }

        /// <summary>
        /// 结果返回时间
        /// </summary>
        public DateTime ResultTime { get; set; }


        /// <summary>
        /// 获取或设置与此操作关联的用户或应用程序对象。
        /// </summary>
        public object UserToKen { get; set; }

        /// <summary>
        /// 用于回调的委托
        /// </summary>
        internal Action<AsyncSendResult> CallBakc { get; set; }
    }
一个服务端只需要一个异步发送[实例 , 不需要为每一个客户端创建一实例
xiaogui340 2015-01-25
  • 打赏
  • 举报
回复
引用 13 楼 JZ_7975 的回复:
[quote=引用 7 楼 xiaogui340 的回复:] 看来server类的定义,没发现有支持并行的啊,从头至尾就一个socket。 下面一段server端代码,供参考

        private IPEndPoint serverInfo;//存放服务器的IP和端口信息
        private Socket serverSocket;//服务端运行的SOCKET
        private Thread serverThread;//服务端运行的线程
        private Socket[] clientSocket;//为客户端建立的SOCKET连接
        private int clientNumb;//存放客户端数量
        private byte[] msgBuffer;//存放消息数据

        private void btnStart_Click(object sender, EventArgs e)
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverInfo = new IPEndPoint(IPAddress.Any, this.GetPort());
            serverSocket.Bind(serverInfo);
            serverSocket.Listen(10);
            clientSocket = new Socket[65535];
            msgBuffer = new byte[65535];
            clientNumb = 0;
            serverThread = new Thread(new ThreadStart(ReceiveAccept));
            serverThread.Start();
            CheckForIllegalCrossThreadCalls = false;//不捕获对错误线程的调用
            this.btnStart.Enabled = false;
            this.btnEnd.Enabled = true;
            this.labelMsg.Text = "服务正在运行..." + " 运行端口:" + this.GetPort().ToString();
            this.lbClients.Items.Add("服务于 " + DateTime.Now.ToString() + " 开始运行.");
        }

        private void ReceiveAccept()
        {
            while (true)
            {
                //Accept 以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket。
                //在阻止模式中,Accept 将一直处于阻止状态,直到传入的连接尝试排入队列。连接被接受后,原来的 Socket 继续将传入的连接请求排入队列,直到您关闭它。
                clientSocket[clientNumb] = serverSocket.Accept();
                clientSocket[clientNumb].BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None,
                    new AsyncCallback(ReceiveCallback), clientSocket[clientNumb]);
                lock (this.lbClients)
                {
                    this.lbClients.Items.Add(clientSocket[clientNumb].RemoteEndPoint.ToString() + " 成功连接服务器.");
                }
                clientNumb++;
            }
        }

        private void ReceiveCallback(IAsyncResult ia)
        {
            try
            {
                Socket s = (Socket)ia.AsyncState;
                int iEnd= s.EndReceive(ia);
                for (int i = 0; i < clientNumb; i++)
                {
                    if (clientSocket[i].Connected)
                        clientSocket[i].Send(msgBuffer, 0, iEnd, SocketFlags.None);
                    s.BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
                }
            }
            catch
            { }
        }
只是在创建服务端的时候,使用了线程,当客户端数量一多的时间,性能能保证吗?[/quote] private Socket[] clientSocket;//为客户端建立的SOCKET连接 这里每一个客户端连接 都是异步互相独立的在接收 来自客户端的请求。创建服务端使用线程,只是为了不堵塞主线程罢了。 至于性能方面,我想无非是客户端的数量越多,对于资源的消耗,服务器是否能吃的消。跟线程关系不大
JZ_7975 2015-01-21
  • 打赏
  • 举报
回复
引用 7 楼 xiaogui340 的回复:
看来server类的定义,没发现有支持并行的啊,从头至尾就一个socket。 下面一段server端代码,供参考

        private IPEndPoint serverInfo;//存放服务器的IP和端口信息
        private Socket serverSocket;//服务端运行的SOCKET
        private Thread serverThread;//服务端运行的线程
        private Socket[] clientSocket;//为客户端建立的SOCKET连接
        private int clientNumb;//存放客户端数量
        private byte[] msgBuffer;//存放消息数据

        private void btnStart_Click(object sender, EventArgs e)
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverInfo = new IPEndPoint(IPAddress.Any, this.GetPort());
            serverSocket.Bind(serverInfo);
            serverSocket.Listen(10);
            clientSocket = new Socket[65535];
            msgBuffer = new byte[65535];
            clientNumb = 0;
            serverThread = new Thread(new ThreadStart(ReceiveAccept));
            serverThread.Start();
            CheckForIllegalCrossThreadCalls = false;//不捕获对错误线程的调用
            this.btnStart.Enabled = false;
            this.btnEnd.Enabled = true;
            this.labelMsg.Text = "服务正在运行..." + " 运行端口:" + this.GetPort().ToString();
            this.lbClients.Items.Add("服务于 " + DateTime.Now.ToString() + " 开始运行.");
        }

        private void ReceiveAccept()
        {
            while (true)
            {
                //Accept 以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket。
                //在阻止模式中,Accept 将一直处于阻止状态,直到传入的连接尝试排入队列。连接被接受后,原来的 Socket 继续将传入的连接请求排入队列,直到您关闭它。
                clientSocket[clientNumb] = serverSocket.Accept();
                clientSocket[clientNumb].BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None,
                    new AsyncCallback(ReceiveCallback), clientSocket[clientNumb]);
                lock (this.lbClients)
                {
                    this.lbClients.Items.Add(clientSocket[clientNumb].RemoteEndPoint.ToString() + " 成功连接服务器.");
                }
                clientNumb++;
            }
        }

        private void ReceiveCallback(IAsyncResult ia)
        {
            try
            {
                Socket s = (Socket)ia.AsyncState;
                int iEnd= s.EndReceive(ia);
                for (int i = 0; i < clientNumb; i++)
                {
                    if (clientSocket[i].Connected)
                        clientSocket[i].Send(msgBuffer, 0, iEnd, SocketFlags.None);
                    s.BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
                }
            }
            catch
            { }
        }
只是在创建服务端的时候,使用了线程,当客户端数量一多的时间,性能能保证吗?
於黾 2015-01-20
  • 打赏
  • 举报
回复
既然是异步怎么还加入了AllDone.Set()同步代码 没事别老阻塞代码
JZ_7975 2015-01-20
  • 打赏
  • 举报
回复
接上文“

      #region 异步发送的回调函数

        /// <summary>
        /// 发送给客户端
        /// </summary>
        /// <param name="handler">socket</param>
        /// <param name="byteData">字节信息</param>
        public static void Send(Socket handler, byte[] byteData)
        {
            // Convert the string data to byte data using ASCII encoding.
            //byte[] byteData = Encoding.ASCII.GetBytes(data);
            string sendMessgae = CommonTools.BytesToString(byteData);
            LogInfo.Instance.SetMessage("INFO", "采集回发放映报文:" + sendMessgae);
            // Begin sending the data to the remote device.
            handler.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), handler);
        }

        public static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                int bytesSent = handler.EndSend(ar);
                LogInfo.Instance.SetMessage("INFO", "发送成功,总字节数:" + bytesSent);
            }
            catch (Exception ex)
            {
                LogError.Instance.SetMessage("ERROR", ex.ToString());
            }
        }
        #endregion
JZ_7975 2015-01-20
  • 打赏
  • 举报
回复
接上文代码:


        #region 异步接收数据的回调函数
        public static void ReadCallback(IAsyncResult ar)
        {
            //缓冲区大小
            StateObject state = null;
            var sb = new StringBuilder();
            byte[] reciveData;
            ModuleSocketInfo msi;
            //线程使用
            SocketActivateCertification socketActivateCertification;
            SocketHeartbeat socketHeartbeat;
            SocketUpdateIP socketUpdateIP;
            SocketTakingPictures socketTakingPictures;
            SocketFile socketFile;
            Thread t;
            try
            {

                state = (StateObject)ar.AsyncState;
                Handler = state.workSocket;
                var moduleIp = Handler.RemoteEndPoint.ToString();//放映机IP
                //客户端发送数据的长度
                var bytesRead = Handler.EndReceive(ar);
                //输出日志信息
                var printData = new byte[bytesRead];
                Array.Copy(state.buffer, 0, printData, 0, bytesRead);
                var printDataInfo = CommonTools.BytesToString(printData);
                LogInfo.Instance.SetMessage("INFO", "原始数据,接收放映机IP:" + moduleIp + ",发送的报文长度为:" + bytesRead + ",数据为:" + printDataInfo);

                if (FrameData == null)//不需要处理粘包问题
                {
                    //数据长度=缓存区长度
                    reciveData = new byte[bytesRead];
                    //数据拷贝
                    Array.Copy(state.buffer, 0, reciveData, 0, bytesRead);
                }
                else//处理粘包问题
                {
                    //数据长度=缓存区长度+粘包数据长度
                    reciveData = new byte[bytesRead + FrameData.Length];
                    //组包数据拷贝到reciveData
                    Array.Copy(FrameData, 0, reciveData, 0, FrameData.Length);
                    //接收数据拷贝到reciveData
                    Array.Copy(state.buffer, 0, reciveData, FrameData.Length, bytesRead);
                    //重新设置为null
                    FrameData = null;
                }

                //接收的数据长度>0
                if (reciveData.Length > 0)
                {

                    for (var i = 0; i < reciveData.Length; i++)
                    {
                        //校验包头,第三个字节为正文长度,如果不做判断,在下面取长度的时候会报错
                        if (reciveData[i] == 0x5A && reciveData.Length > 3)
                        {
                            //报文长度
                            var messageLength = reciveData[i + 2] + 9; //9表示帧头和帧尾长度
                            //接收的报文长度>=实际报文长度
                            if (reciveData.Length >= messageLength)
                            {
                                //定义报文长度
                                var tempMessage = new byte[messageLength];
                                frameMessageLength += tempMessage.Length; //存储帧长度
                                //赋值报文(将原来的i改成了现在的0,主要是因为碰到5A。。。。5A时有问题)
                                Array.Copy(reciveData, i, tempMessage, 0, tempMessage.Length);
                                //完整的一帧报文
                                var fullMesssage = CommonTools.BytesToString(tempMessage);
                                LogInfo.Instance.SetMessage("INFO",
                                                            "逻辑处理,接收放映机IP:" + moduleIp + ",一帧完整的报文:" + fullMesssage);

                                //-------待优化---------
                                //线程只是创建一次,数据存储在队列中
                                lock (SocketServer.HtFyjInfo)
                                {
                                    LogInfo.Instance.SetMessage("INFO", "SocketServer");
                                    PrintHtFyjInfo();
                                    if (SocketServer.HtFyjInfo.Count > 0)
                                    {
                                        if (SocketServer.HtFyjInfo.Contains(moduleIp))
                                        {
                                            msi = (ModuleSocketInfo)SocketServer.HtFyjInfo[moduleIp];
                                            if (msi != null)
                                            {
                                                //校验包头
                                                if (tempMessage[0] == 0x5A)
                                                {
                                                    //报文类型
                                                    switch (tempMessage[1])
                                                    {
                                                        case 0x01: //模块请求激活报文
                                                            //实例化
                                                            msi.message0x01 = tempMessage;
                                                            //赋值
                                                            //Array.Copy(reciveData, 0, msi.message0x01, 0,
                                                            //           reciveData.Length);
                                                            //调用线程
                                                            socketActivateCertification =
                                                                new SocketActivateCertification(msi.message0x01,
                                                                                                moduleIp);
                                                            t =
                                                                new Thread(
                                                                    new ThreadStart(
                                                                        socketActivateCertification
                                                                            .ThreadParsingMessage0x01));
                                                            t.IsBackground = true;
                                                            t.Start();
                                                            break;
                                                         //其他报文也是同样处理,接收到数据后,启动一个线程来处理
                                                    }
                                                }
                                                else
                                                {
                                                    LogInfo.Instance.SetMessage("INFO",
                                                                                "包头校验错误,数据抛弃,错误包头为:" + reciveData[0]);
                                                }
                                            }
                                            else
                                            {
                                                LogInfo.Instance.SetMessage("INFO", "集合中没有放映机:" + moduleIp + "的记录");
                                            }
                                        }
                                    }
                                }
                                //-------待优化---------


                                //判断是否存在下一帧
                                var nextFrame = reciveData.Length - frameMessageLength;
                                if (nextFrame != 0) //存在下一帧
                                {
                                    i = messageLength - 1; //for循环的时候i会++

                                }
                                else
                                {
                                    i = frameMessageLength; //从一个完整的帧开始循环
                                    frameMessageLength = 0; //在一个完整的帧的时候,重置为0
                                }
                            }
                            else //接收的报文长度<实际报文长度
                            {
                                //存储在组包数据中
                                FrameData = new byte[reciveData.Length];
                                Array.Copy(reciveData, 0, FrameData, 0, FrameData.Length);
                                break;
                            }
                        }
                        else
                        {
                            LogInfo.Instance.SetMessage("INFO", "包头校验错误数据抛弃,包头为:" + reciveData[i]);
                          
                        }
                    }
                    //if (Handler != null && Handler.Connected)
                    //{

                    //}

                    ////继续异步接收
                    Handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReadCallback), state);
                }
                else
                {
                    //使用测试工具的时候,会进去此逻辑
                    LogInfo.Instance.SetMessage("INFO", "放映机:" + moduleIp + ",断开连接");
                    SocketServer.HtFyjInfo.Remove(Handler.RemoteEndPoint.ToString());
                    LogInfo.Instance.SetMessage("INFO", "集合中删除:" + Handler.RemoteEndPoint.ToString());
                    PrintHtFyjInfo();
                }
                
            }
            catch (SocketException ex)
            {
               
            }
            catch (ArgumentException ex)
            {
                
            }
            catch (Exception ex)
            {
                
            }
        }
        #endregion
JZ_7975 2015-01-20
  • 打赏
  • 举报
回复
引用 11 楼 Z65443344 的回复:
你仅仅把IP存下来有什么用,等你想给客户端发消息的时候上哪里去找对应的socket(已经丢弃了),难道你要服务端主动去连接客户端?
不是,我是这样处理的

    var handler = listener.EndAccept(ar);
                //客户端IP
                var clientIp = IPAddress.Parse(((IPEndPoint)handler.RemoteEndPoint).Address.ToString()) + ":" + ((IPEndPoint)handler.RemoteEndPoint).Port.ToString(CultureInfo.InvariantCulture);
                //信息显示
                var clientRunInfo = "放映机:" + clientIp + "与数据采集已连接";
                LogInfo.Instance.SetMessage("INFO", clientRunInfo);
                MainForm.AppendRunInfo(clientRunInfo);

                //将和采集建立连接的放映机存储至哈希表中
                var mss = new ModuleSocketInfo()
                {
                    handleSocket = handler,
                    connectionState = "0"//未连接,仅仅是建立socket连接,而非和放映机建立连接
                };
                mss.CumulativeSerialNumber();
                HtFyjInfo.Add(clientIp, mss);
於黾 2015-01-20
  • 打赏
  • 举报
回复
你仅仅把IP存下来有什么用,等你想给客户端发消息的时候上哪里去找对应的socket(已经丢弃了),难道你要服务端主动去连接客户端?
JZ_7975 2015-01-20
  • 打赏
  • 举报
回复
引用 9 楼 Z65443344 的回复:
[quote=引用 6 楼 JZ_7975 的回复:] [quote=引用 4 楼 sp1234 的回复:] 如果你说你使用并发处理机制,那么就这一堆定义
        public static ManualResetEvent AllDone = new ManualResetEvent(false);
        //存储客户端IP和Message0x75报文
        public static Hashtable HtMessaeg0X75 = new Hashtable();
        //存储放映机信息
        public static Hashtable HtFyjInfo = new Hashtable();
        public static Socket Handler = null;
        public static byte[] FrameData;//帧数据,用于粘包处理
        private static int frameMessageLength = 0;

它就很成问题。 Hashtable过于原始和含糊,你应该具有更加明确强类型的定义。 客户端有多少个,Buffer也就有多个。怎么可能只有一个Socket和一个Buffer呢?
也就是说,我需要将所有的socket连接存储在Hashtable中,然后进行区分处理[/quote] 我觉得你有必要了解一下线程池的概念,socket完全可以是动态new出来,而不是事先定义好的 即使是动态new出来的socket想要保存句柄,也完全可以使用List<socket>之类的结构来存放,而不是什么都用Hashtable存[/quote] 谢谢指点,我稍后了解下线程池 之所以有用Hashtable存储是因为key存储的是客户端的连接IP,客户端本身是不带有编号信息的, 为了方便查找才用Hashtable存储的
於黾 2015-01-20
  • 打赏
  • 举报
回复
引用 6 楼 JZ_7975 的回复:
[quote=引用 4 楼 sp1234 的回复:] 如果你说你使用并发处理机制,那么就这一堆定义
        public static ManualResetEvent AllDone = new ManualResetEvent(false);
        //存储客户端IP和Message0x75报文
        public static Hashtable HtMessaeg0X75 = new Hashtable();
        //存储放映机信息
        public static Hashtable HtFyjInfo = new Hashtable();
        public static Socket Handler = null;
        public static byte[] FrameData;//帧数据,用于粘包处理
        private static int frameMessageLength = 0;

它就很成问题。 Hashtable过于原始和含糊,你应该具有更加明确强类型的定义。 客户端有多少个,Buffer也就有多个。怎么可能只有一个Socket和一个Buffer呢?
也就是说,我需要将所有的socket连接存储在Hashtable中,然后进行区分处理[/quote] 我觉得你有必要了解一下线程池的概念,socket完全可以是动态new出来,而不是事先定义好的 即使是动态new出来的socket想要保存句柄,也完全可以使用List<socket>之类的结构来存放,而不是什么都用Hashtable存
JZ_7975 2015-01-20
  • 打赏
  • 举报
回复
引用 5 楼 Z65443344 的回复:
所以所谓"并发","异步",都是楼主自己在骗自己 其实既没法并发也不是异步的 你能骗自己,但是骗不了编译器 只有一个静态socket,那么当然一个客户端在发送的时候突然有另一个客户端连进来,它就指向另一个客户端,而把前一个客户端的句柄给丢弃了
需要对每一个连接的socket进行存储,区分,对吗?
xiaogui340 2015-01-20
  • 打赏
  • 举报
回复
看来server类的定义,没发现有支持并行的啊,从头至尾就一个socket。 下面一段server端代码,供参考

        private IPEndPoint serverInfo;//存放服务器的IP和端口信息
        private Socket serverSocket;//服务端运行的SOCKET
        private Thread serverThread;//服务端运行的线程
        private Socket[] clientSocket;//为客户端建立的SOCKET连接
        private int clientNumb;//存放客户端数量
        private byte[] msgBuffer;//存放消息数据

        private void btnStart_Click(object sender, EventArgs e)
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverInfo = new IPEndPoint(IPAddress.Any, this.GetPort());
            serverSocket.Bind(serverInfo);
            serverSocket.Listen(10);
            clientSocket = new Socket[65535];
            msgBuffer = new byte[65535];
            clientNumb = 0;
            serverThread = new Thread(new ThreadStart(ReceiveAccept));
            serverThread.Start();
            CheckForIllegalCrossThreadCalls = false;//不捕获对错误线程的调用
            this.btnStart.Enabled = false;
            this.btnEnd.Enabled = true;
            this.labelMsg.Text = "服务正在运行..." + " 运行端口:" + this.GetPort().ToString();
            this.lbClients.Items.Add("服务于 " + DateTime.Now.ToString() + " 开始运行.");
        }

        private void ReceiveAccept()
        {
            while (true)
            {
                //Accept 以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket。
                //在阻止模式中,Accept 将一直处于阻止状态,直到传入的连接尝试排入队列。连接被接受后,原来的 Socket 继续将传入的连接请求排入队列,直到您关闭它。
                clientSocket[clientNumb] = serverSocket.Accept();
                clientSocket[clientNumb].BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None,
                    new AsyncCallback(ReceiveCallback), clientSocket[clientNumb]);
                lock (this.lbClients)
                {
                    this.lbClients.Items.Add(clientSocket[clientNumb].RemoteEndPoint.ToString() + " 成功连接服务器.");
                }
                clientNumb++;
            }
        }

        private void ReceiveCallback(IAsyncResult ia)
        {
            try
            {
                Socket s = (Socket)ia.AsyncState;
                int iEnd= s.EndReceive(ia);
                for (int i = 0; i < clientNumb; i++)
                {
                    if (clientSocket[i].Connected)
                        clientSocket[i].Send(msgBuffer, 0, iEnd, SocketFlags.None);
                    s.BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
                }
            }
            catch
            { }
        }
JZ_7975 2015-01-20
  • 打赏
  • 举报
回复
引用 4 楼 sp1234 的回复:
如果你说你使用并发处理机制,那么就这一堆定义
        public static ManualResetEvent AllDone = new ManualResetEvent(false);
        //存储客户端IP和Message0x75报文
        public static Hashtable HtMessaeg0X75 = new Hashtable();
        //存储放映机信息
        public static Hashtable HtFyjInfo = new Hashtable();
        public static Socket Handler = null;
        public static byte[] FrameData;//帧数据,用于粘包处理
        private static int frameMessageLength = 0;

它就很成问题。 Hashtable过于原始和含糊,你应该具有更加明确强类型的定义。 客户端有多少个,Buffer也就有多个。怎么可能只有一个Socket和一个Buffer呢?
也就是说,我需要将所有的socket连接存储在Hashtable中,然后进行区分处理
於黾 2015-01-20
  • 打赏
  • 举报
回复
所以所谓"并发","异步",都是楼主自己在骗自己 其实既没法并发也不是异步的 你能骗自己,但是骗不了编译器 只有一个静态socket,那么当然一个客户端在发送的时候突然有另一个客户端连进来,它就指向另一个客户端,而把前一个客户端的句柄给丢弃了
  • 打赏
  • 举报
回复
如果你说你使用并发处理机制,那么就这一堆定义
        public static ManualResetEvent AllDone = new ManualResetEvent(false);
        //存储客户端IP和Message0x75报文
        public static Hashtable HtMessaeg0X75 = new Hashtable();
        //存储放映机信息
        public static Hashtable HtFyjInfo = new Hashtable();
        public static Socket Handler = null;
        public static byte[] FrameData;//帧数据,用于粘包处理
        private static int frameMessageLength = 0;

它就很成问题。 Hashtable过于原始和含糊,你应该具有更加明确强类型的定义。 客户端有多少个,Buffer也就有多个。怎么可能只有一个Socket和一个Buffer呢?

111,125

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧