异步Socket通信编程的C#实现(2)

在第一篇中,已经实现了异步通信的控制台应用程序。服务端能够连接多个客户端并发送消息。

在这一篇中,将实现两个功能:
1. 实现GUI的客户端,能够与服务器进行双向通信
2. 实现socket通信的长度校验

客户端的GUI实现

在控制台程序中,客户端不能方便的发送消息给服务器,使用C#编写用户界面,实现通信和打印消息。
这里写图片描述
在GUI的socket编程中,必须使用异步编程,或者多线程编程,否则主线程在进行通信时就无法刷新界面。

发送消息
点击发送键将发送文本框的内容,这里不是连续发送,不需要使用异步编程。

      public bool sendMsg(string message)
       {
           try
           {
               msg = message;
               sendBuffer = Encoding.Unicode.GetBytes(message);
               cSocket.Send(sendBuffer, sendBuffer.Length, 0);
           }
           catch (Exception e)
           {
               msg = "send exception: " + e.Message;
           }            
           return true;
       }

实现效果
这里写图片描述

左边是服务器,右边是客户端。点击按钮连接服务器,开始接收服务器定时发送的信息。在文本框输入信息,点击按键发送到服务端。

长度校验

TCP/IP协议在进行数据发送的时候,并不是严格按照编程写的一条一条发送,为了提高效率,会有如下的改变:
1. 当数据长度较短时,会把数据累计到一定的长度后发送,一次接收的可能是几条数据;
2. 当数据长度过长的时,会把数据拆成几条进行发送。

但TCP/IP协议保证这些数据是有序到达而且不会丢失,是可靠的。

解决方案
因此,为了将接收的信息进行正确的解析,我们必须对数据进行一定的包装操作,以正确组装接收的信息。
1. 在每一个数据前添加一个长度,以特殊符号进行标识,在这里使用[];
2. 由于在数据中可能含有特殊符号,会对标记进行干扰,因此可以使用一定的可逆编码技术,编码后保证不会出现我们标记的字符,如base64编码。

编码

        public string createStr()
        {
            string s = "";
            if (sr.Peek()>=0)       // 判断结尾
            {
                s = sr.ReadLine();
                // 进行base64编码
                byte[] b = Encoding.Unicode.GetBytes(s);
                s = Convert.ToBase64String(b);
                s = "[" + s.Length + "]" + s;
            }                        
            return s;
        }

长度校验

长度校验使用标记字段进行校验
1. 如果之前一条已经解析完毕,查找[]获得长度,然后进行装配
2. 如果之前一条未解析完毕,根据还缺的长度进行装配查找

这里写的不是很完善,一些情况会出问题:
1. 比如标识字段被分成两条时,下面代码无法进行解析

        private bool complete;  // 之前解析是否完成
        private int strLength;  // 字符串长度
        private string unStr;   // 记录已经累计的长度
        public bool writeToFile(string s)
        {
            sw_o.WriteLine(s);
            sw_o.Flush();
            int idx1, idx2;
            try
            {               
                if(complete)    // 上一轮已经查找完毕
                {
                    idx1 = s.IndexOf('[');
                    idx2 = s.IndexOf(']');
                    while(idx1!=-1)  //能够找到一个标识符
                    {
                        // 获取长度
                        strLength = int.Parse(s.Substring(idx1 + 1, idx2 - idx1 - 1));
                        Console.WriteLine(strLength);
                        if (s.Length - idx2 - 1 < strLength)     // 这一个段已经不够使用
                        {
                            unStr = s.Substring(idx2 + 1, s.Length - idx2 - 1);
                            strLength -= (s.Length - idx2 - 1);
                            complete = false;
                            break;
                        }
                        else if (s.Length - idx2 - 1 == strLength)  // 刚好够用
                        {
                            unStr = s.Substring(idx2 + 1, strLength);
                            string str = decoderBase64(unStr);
                            writeStr(str);
                            Console.WriteLine("finished1: " + str);
                            complete = true;
                            break;
                        }
                        else
                        {
                            string str = s.Substring(idx2 + 1, strLength); // 写入一个完整字符串
                            str = decoderBase64(str);
                            writeStr(str);
                            Console.WriteLine("finished2: " + str);
                            // 截取新的字符串进行重复操作
                            s = s.Substring(idx2 + strLength + 1, s.Length - idx2 - strLength - 1);
                            idx1 = s.IndexOf('[');
                            idx2 = s.IndexOf(']');
                        }                                           
                    }
                }
                else
                {
                    if(s.Length<strLength)      // 不够使用
                    {
                        strLength -= s.Length;
                        unStr += s;
                        complete = false;
                    }
                    else if(s.Length == strLength)      // 刚好完全
                    {
                        complete = true;
                        unStr += s;
                        string str = decoderBase64(unStr);
                        writeStr(str);
                        Console.WriteLine("finished3: "+str);
                    }
                    else
                    {
                        idx1 = s.IndexOf('[');
                        unStr += s.Substring(0, idx1);      // 上次遗留的进行完全
                        string str = decoderBase64(unStr);
                        writeStr(str);
                        idx2 = s.IndexOf(']');
                        while(idx1 !=-1)
                        {
                            // 获取长度
                            strLength = int.Parse(s.Substring(idx1 + 1, idx2 - idx1 - 1));
                            Console.WriteLine(strLength);
                            if (s.Length - idx2 - 1 < strLength)     // 这一个段已经不够使用
                            {
                                unStr = s.Substring(idx2 + 1, s.Length - idx2 - 1);
                                strLength -= (s.Length - idx2 - 1);
                                complete = false;
                                break;
                            }
                            else if (s.Length - idx2 - 1 == strLength)  // 刚好够用
                            {
                                unStr = s.Substring(idx2 + 1, strLength);
                                str = decoderBase64(unStr);
                                writeStr(str);
                                Console.WriteLine("finished4: " + str);
                                complete = true;
                                break;
                            }
                            else
                            {
                                str = s.Substring(idx2 + 1, strLength); // 写入一个完整字符串
                                str = decoderBase64(str);
                                writeStr(str);
                                Console.WriteLine("finished5: " + str);
                                // 截取新的字符串进行重复操作
                                s = s.Substring(idx2 + strLength + 1, s.Length - idx2 - strLength - 1);
                                idx1 = s.IndexOf('[');
                                idx2 = s.IndexOf(']');
                            }                               
                        }
                    }
                }                       
            }
            catch(Exception e)
            {
                Console.WriteLine("writefile Exception: "+e.Message);
            }
            return true;

        }

实现效果

这里写图片描述
左边是组装后的文本,右边是实际接收到的经过base64编码的,TCP/IP协议自动拆分合并的,实际发送的数据。对于这种情况能够进行正确的组装。

欢迎补充更完善的校验~

猜你喜欢

转载自blog.csdn.net/hu694028833/article/details/78882823