c#socket实现对序列化对象的传输和接收

记得一年前做了一个简单的套接字通讯的小软件,但是存在一个不小的bug:发送消息和发送文件的时候采用的区分方法是在发送正文钱需要首先发送一个简短的字符(file表示将要文件,text表示将要发送文本消息),数据发送完成以后还发送了一个“end”字符串来表示内容发送完成。结果导致程序很不稳定,特别是发送文件的时候很容易导致文件接收的不完整。最近看书的时候发现作者采用的是传输序列化对象的办法,这样就相当于是制定了一套自己的“通信协议”。下面是代码:

1.保存消息数据的类:

/// <summary>
    /// 消息命令类型
    /// </summary>
    public enum msgCommand
    {
       /// <summary>
        /// 空
        /// </summary>
        None,       
        /// <summary>
        /// 登录
        /// </summary>
        Login,      
       /// <summary>
        /// 退出
        /// </summary>
        Exit,       
        /// <summary>
        /// 更改密码
        /// </summary>
        ChangePwd,  
       /// <summary>
        /// 注册
        /// </summary>
        Register,   
        /// <summary>
        /// 获取在线用户列表
        /// </summary>
        GetUserList,
        /// <summary>
        /// 获取通知
        /// </summary>
        GetNotice  
    }
   /// <summary>
    /// 消息类型
    /// </summary>
    public enum msgType
    {
        /// <summary>
        ///空消息
        /// </summary>
        None,
        /// <summary>
        ///文本消息
        /// </summary>
        SendText,
        /// <summary>
        ///发送文件
        /// </summary>
        SendFile
    }
   /// <summary>
    /// 消息发送状态(用于消息循环接收的开始和技术标识)
    /// </summary>
    public enum msgSendState
    {
        None,   //空
        single, //单条消息,一次性接收
        start,  //消息开始
        sending,//消息发送中
        end     //消息结束
    }
    /// <summary>
    /// 消息类,标记为可序列化
    /// </summary>
    [Serializable]
    public class ClassMsg
    {
        public string sendIP = string.Empty;               //发送方IP
        public string sendProt = string.Empty;             //发送方监听端口
        public msgCommand command = msgCommand.None;       //命令枚举
        public msgType type = msgType.None;                //消息类型枚举
        public msgSendState msgState = msgSendState.None;  //消息状态枚举
        public byte[] Data;                                //消息内容
    }


2.客户端:(只写了发送的方法)

  

class tcpSocketClient
    {
        public void sendMsg()
        {
            IPAddress IP = Dns.GetHostAddresses(Dns.GetHostName())[3];
            while (true)
            {
                Console.WriteLine("enter the msg:");
                Socket socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                IPEndPoint Ip = new IPEndPoint(IP, 3000);
                socketSend.Connect(Ip);
                ClassMsg msg = new ClassMsg();
                string strMsg = Console.ReadLine();
                msg.type=msgType.SendText;
                msg.sendIP=Dns.GetHostAddresses(Dns.GetHostName())[3].ToString();
                msg.Data = Encoding.Default.GetBytes(strMsg);
                MemoryStream mStream = new MemoryStream();
                BinaryFormatter bformatter = new BinaryFormatter();  //二进制序列化类
                bformatter.Serialize(mStream, msg); //将消息类转换为内存流
                mStream.Flush();
                byte[] buffer = new byte[1024];
                mStream.Position = 0;  //将流的当前位置重新归0,否则Read方法将读取不到任何数据
                while (mStream.Read(buffer, 0, buffer.Length) > 0)
                {
                    socketSend.Send(buffer); //从内存中读取二进制流,并发送
                }
                socketSend.Close();
                Console.WriteLine("消息发送完毕!输入Y退出,其他继续:");
                string strEnter = Console.ReadLine();
                if (strEnter == "Y" || strEnter == "y")
                {
                    break;
                }
            }
        }
    }

3.服务器端:(只写了循环接收)

监听方法:

  

	public void Listen()
        {
            Socket tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            IPEndPoint IP = new IPEndPoint(Dns.GetHostAddresses(Dns.GetHostName())[3], 3000);
            Console.WriteLine("监听已启动" + Dns.GetHostAddresses(Dns.GetHostName())[3]);
            tcpSocket.Bind(IP);
            tcpSocket.Listen(10);
            while (true)
            {
                Socket socketAccpet = tcpSocket.Accept();
                TcpSocketReceive re = new TcpSocketReceive(socketAccpet);
                Thread thread = new Thread(new ThreadStart(re.ReceiveMsg));
                thread.Start();//可能有并发请求,所以消息的接收也需要在子线程中处理
            }
        }

说明:因为Socket的Listen方法会一直处于阻塞状态,所以不能够在主线程直接使用,否则应用程序将不能再做其他事情。所以有了下面的另外一个Listen方法。

        

	public void Listen()
        {
            TcpSocket Tsocket = new TcpSocket();
            Thread TcpThread = new Thread(new ThreadStart(Tsocket.Listen));
            TcpThread.Start();//启动一个子线程来调用socket的监听方法
        }
        /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="socketAccept"></param>
        public void ReceiveMsg()
        {
            byte[] buffer = new byte[1024];
            MemoryStream mStream = new MemoryStream();
            mStream.Position = 0;
            while (true)
            {
                int ReceiveCount = socketAccept.Receive(buffer,1024,0);
                if (ReceiveCount ==0)
                {
                    break;//接收到的字节数为0时break
                }
                else
                {
                    Console.WriteLine("成功获取到数据");
                    mStream.Write(buffer,0,ReceiveCount); //将接收到的数据写入内存流
                }
            }
            mStream.Flush();
            mStream.Position = 0;
            BinaryFormatter bFormatter = new BinaryFormatter();
            if (mStream.Capacity > 0)
            {
                ClassMsg msg = (ClassMsg)bFormatter.Deserialize(mStream);//将接收到的内存流反序列化为对象
                Console.WriteLine("接收到来自"+msg.sendIP+"的信息内容:"+Encoding.Default.GetString(msg.Data));
            }
            else
            {
                Console.WriteLine("接收到的数据为空。");
            }
            socketAccept.Close();
            Console.WriteLine("线程执行完毕。");
        }


  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值