我希望实现的功能是服务器把消息、播放列表和列表中所列的图片和视频都发给客户端。在写发送文件的时候遇到了几个问题。
1.传送xml文件时,传给客户端,打开后总是有这样一条提示【字符''(十六进制值0x0)在xml文档中是非法的】。
2.传送的图片和视频,在客户端都无法打开,奇怪的是我收到的包总是比发的要多。server发送:FileStream fsimg = new FileStream(System.Windows.Forms.Application.StartupPath + "\\MediaLists.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
string strHead = "P" + ((char)191).ToString() + "MediaLists.xml" + ((char)191).ToString();
int bigsize = 10000;
long fsleng = fsimg.Length;
double bnum = fsleng / bigsize;
bnum = Math.Floor(bnum + 1);
strHead += bnum.ToString() + ((char)191).ToString();
int bytes = 0;
int bytes_num = 0;
byte[] bytedata = new byte[bigsize];
Socket s = (Socket)slist[i];
Send(s, strHead);
while (bytes_num < fsleng)
{
    bytes = fs.Read(bytedata, 0, bigsize);
    string strsend = Encoding.UTF8.GetString(bytedata);
    strsend = strsend.Replace("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]", "*");
    bytedata = Encoding.UTF8.GetBytes(strsend);
    Send(s, bytedata);
    bytes_num += bytes;
}
Send(s, ((char)191).ToString() + "O");client接收:private void ReadCallback(IAsyncResult ai)
{
    try
    {
        StateObject state = (StateObject)ai.AsyncState;
        Socket client = state.workSocket;
        int byteread = client.EndReceive(ai);
        if (byteread > 0)
        {
                    state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, byteread));
                    listbyte.Add(state.buffer);
        }        switch (state.sb.ToString().Substring(0, 1))
        {
            case "P":
                //对文件的处理
                string s = state.sb.ToString();
                string strOver = s.Substring(s.Length - 1);
                string[] strRecr = state.sb.ToString().Split((char)191);
                string strFileName = strRecr[1].ToString();
                int intNum = Convert.ToInt32(strRecr[2].ToString());
                if (strOver == "O")
                {
                    FileStream fsRecv = File.Create(System.Windows.Forms.Application.StartupPath + "\\" + strFileName);
                    for (int i = 1; i < listbyte.Count-1; i++)
                    {
                                byte[] byteB = (byte[])listbyte[i];
                                fsRecv.Write(byteB,0,byteB.Length);
                    }
                    fsRecv.Close();
                    listbyte.Clear();
                    state.sb.Remove(0, state.sb.Length);
                }
                break;
            case "M":
                 //对消息的处理
                 state.sb.Remove(0, state.sb.Length);
                 listbyte.Clear();
                 break;
        }
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
        }
        catch () { };
    }
}

解决方案 »

  1.   

    个人写的发送和接收的封装类(TCP的) public class TcpSendReceive
        {
            NetworkStream _Ns;        const int Block = 500 * 1024;        public delegate void SendFileProgressChangedCallback(object sender, ProgressChangedEventArgs e);
            public delegate void ReceiveFileProgressChangedCallback(object sender, ProgressChangedEventArgs e);
          
            public event SendFileProgressChangedCallback SendFileProgressChanged;
            public event ReceiveFileProgressChangedCallback ReceiveFileProgressChanged;        public TcpSendReceive(NetworkStream ns)
            {
                _Ns = ns;
            }        public bool SendInt(int value)
            {
                byte[] buffer= BitConverter.GetBytes(value);
                _Ns.Write(buffer, 0, buffer.Length);
                return true;
            }        public int ReceiveInt()
            {
                byte[] buffer = new byte[4];
                _Ns.Read(buffer, 0, 4);
                return BitConverter.ToInt32(buffer, 0);
            }        public bool SendLong(long value)
            {
                byte[] buffer =BitConverter.GetBytes(value);
                _Ns.Write(buffer, 0, buffer.Length);
                return true;
            }        public long ReceiveLong()
            {
                byte[] buffer = new byte[8];
                _Ns.Read(buffer, 0, 8);
                return BitConverter.ToInt64(buffer, 0);
            }        public bool SendString(string str)
            {
                byte[] buffer = Encoding.UTF8.GetBytes(str);
                byte[] resultBuffer = new byte[8 + buffer.Length];
                BitConverter.GetBytes(buffer.Length).CopyTo(resultBuffer, 0);
                buffer.CopyTo(resultBuffer, 8);
                _Ns.Write(resultBuffer, 0, resultBuffer.Length);
                return true;
            }        public string ReceiveString()
            {
                long length = ReceiveLong(); 
                int current;
                if (length > Block)
                {
                    current = Block;
                }
                else
                {
                    current =(int)length;
                }            byte[] buffer=new byte[current];            int read = _Ns.Read(buffer, 0, current);            long receive = read;            byte[] resultBuffer = new byte[length];         
                buffer.CopyTo(resultBuffer, 0);            while (read > 0 && length > receive)
                {
                    if ((length - receive) > Block)
                    {
                        current = Block;
                    }
                    else
                    {
                        current =(int)( length - receive);
                    }                buffer = new byte[current];                read = _Ns.Read(buffer, 0, current);                receive += read;                buffer.CopyTo(resultBuffer, 0);
                }            return Encoding.UTF8.GetString(resultBuffer);
            }        public bool SendFile(string filePath)
            {
                byte[] length = new byte[8];            FileInfo fi=new FileInfo(filePath);
                BitConverter.GetBytes(fi.Length).CopyTo(length, 0);
                _Ns.Write(length, 0, 8);            int read;
                byte[] buffer = new byte[Block];            ProgressChangedEventArgs e=new ProgressChangedEventArgs();
                e.Length = fi.Length;                      using (FileStream fs = File.Open(filePath, FileMode.Open))
                {
                 
                    while ((read = fs.Read(buffer, 0, Block)) > 0)
                    {
                        
                        _Ns.Write(buffer, 0, read);
                        e.Current += read;                    if (SendFileProgressChanged != null)
                        {
                            SendFileProgressChanged(this, e);
                        }
                    }
                }
                return true;
            }        public bool ReceiveFile(string filePath)
            {
                long length = ReceiveLong();            long receive;
      
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }
                string path = new FileInfo(filePath).Directory.FullName;
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }            using (FileStream fs = File.Open(filePath, FileMode.OpenOrCreate))
                {
                    int current;
                    byte[] buffer = new byte[Block];
                    if (length > Block)
                    {
                        current = Block;
                    }
                    else
                    {
                        current = (int)length;
                    }                int read = _Ns.Read(buffer, 0, Block);
                    receive = read;
                    fs.Write(buffer, 0, read);                ProgressChangedEventArgs e = new ProgressChangedEventArgs();
                    e.Length = length;
                    e.Current = receive;                if (ReceiveFileProgressChanged != null)
                    {
                        ReceiveFileProgressChanged(this, e);
                    }                while (read > 0 && length > receive)
                    {
                        if ((length - receive) > Block)
                        {
                            current = Block;
                        }
                        else
                        {
                            current = (int)(length - receive);
                        }                    read = _Ns.Read(buffer, 0, current);
                        receive += read;
                        fs.Write(buffer, 0, read);                    e.Current = receive;                    if (ReceiveFileProgressChanged != null)
                        {
                            ReceiveFileProgressChanged(this, e);
                        }
                    }
                }
                return true;
            }
        }//end class    public class ProgressChangedEventArgs : EventArgs
        {
            long _Length;
            long _Current;        public long Length
            {
                get { return _Length; }
                set { _Length = value; }
            }        public long Current
            {
                get { return _Current; }
                set { _Current = value; }
            }
        }
      

  2.   

    好乱,不是一般的乱,“slist[i]”这个数组让我简直为你捏把汗。不过说点实际的吧:你的代码    bytes = fs.Read(bytedata, 0, bigsize);
        string strsend = Encoding.UTF8.GetString(bytedata);
    这里的fs我就找不到你的根据,不知道fs是什么,它读出的字节数怎么又受fsimg.Length的限制了呢?bytes完全可能比bigsize小,你怎么能不考虑这种情况而将整个bytedata转换为strsend?而bigsize只是“断章取义”地截取,有可能其最后的字节就切到unicode字符串中一个字符的中间,怎么能随便将bytedate就转成字符串了呢?哎呀,刚刚随便看了几行代码,每一行都至少有一两个明显逻辑错误,实在看不下去了。学好逻辑,学好结构和算法,写出的代码应该有清晰和可靠的基本章法,这样才好真正去工作。
      

  3.   

    更正一下:
    FileStream fsimg = new FileStream(System.Windows.Forms.Application.StartupPath + "\\Vistalan.jpg", FileMode.Open, FileAccess.Read, FileShare.Read);
    应该是:
    FileStream fs = new FileStream(System.Windows.Forms.Application.StartupPath + "\\MediaLists.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
      

  4.   


    既然是xml文件,并且假设是utf8编码的文件,实际上你可以直接用一句
    string strsend2=File.ReadAllText(path);读取出来。而要发送的内容整个组合到一个string中去,然后进需要一条send语句就可以发出。这样,你的发送端的代码至少可以减少一多半。做重要地是精简的代码往往更不同意出现逻辑错误。
       
      

  5.   

    对于接收端(我只是简单地看了几眼),实际上也有跟发送同样的问题,怎么随便把state.buffer转换为字符串呢?并不能保证这一点缓冲的前后不是正好切Unicode半个字符。所以必须将所有buffer收集在一个byte[]中(例如可以先放入一个MemoryStream中最后再转成byte[]),然后才能统一一次性地转换为字符串。而解析动作,只有在你收到消息结束时才开始,而不应该在每一次调用ReadCallback时都解析。
      

  6.   

    socket 只知道字节, byte , 不知道 char, string.
      

  7.   

    另外,你的接收端似乎不管是否已经达到消息结束,都会执行client.BeginReceive那么什么时候不再执行它呢?
    如果你是使用tcp,在.net中使用TcpListener更好一些。当接收时,使用NetwrokStream的DataAvailable属性要比判断缓冲区的有效字符数更准确。有时候缓冲区没有接收到数据,但是DataAvailable可以正确地告诉你数据还有数据。
      

  8.   

    另外你的解析(同时也就是你的应用层协议设计),实在是不能那么想当然啊。例如你去判断if (strOver == "O")
    那么sb的最后一个字符恰好符合,可是并没有到最后一个真实“O”而是xml文件中或者图片中的“O”呢?至于你在解析中判断“M”,我猜你没有写下去。实际这又是考验你协议设计的另一个问题。视频内容可不是xml文件中的字符串。
      

  9.   

    另外,不论是文件,还是视频,业务对象往往都有很多字段属性。在我们开发中,最常遇到的问题是我们经常不断增加、删除属性,甚至修改属性的类型,而那种一开始就死板地在应用协议中设计的方法越来越不适应现代的软件开发。.net有许多将对象与字符串或者二进制进行序列化/反序列化的机制,只要在class上定义好,就可以一下子转换为xml string或者byte[],根本不需要写你这类代码。
      

  10.   

    发送的代码FileStream fs = new FileStream(System.Windows.Forms.Application.StartupPath + "\\ss.txt", FileMode.Open);
    string strHead = "";
    int totalLength = (int)fs.Length; //文件总长度
    int packetsSize = 64 * 1024; //包的大小
    int packetsNum = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(fs.Length) / Convert.ToDouble(packetsSize))); //包的数量
    int currentLength = 0; //当前包的大小
    strHead = "P" + (char)191 + "ss.txt" + (char)191 + totalLength + (char)191 + packetsSize + (char)191 + packetsNum + (char)191 + (totalLength - (packetsNum - 1) * packetsSize).ToString();
    byte[] byteH = System.Text.Encoding.Unicode.GetBytes(strHead.ToCharArray(0, strHead.Length));
    string strF = "<over>";
    byte[] byteF = System.Text.Encoding.Unicode.GetBytes(strF.ToCharArray(0, strF.Length));
    byte[] byteBuffer = null;
    for (int i = 0; i < slist.Count; i++)
    {
                    Socket s = (Socket)slist[i];
                    Send(s, byteH);
                    for (int j = 0; j < packetsNum; j++)
                    {
                        if (totalLength > packetsSize)
                        {
                            currentLength = packetsSize;
                            totalLength -= currentLength;
                        }
                        else
                        {
                            currentLength = totalLength;
                        }
                        byteBuffer = new byte[currentLength];
                        count = fs.Read(byteBuffer, 0, currentLength);
                        byte[] SendingBuffer = new byte[currentLength];
                        SendingBuffer = byteBuffer;
                        Send(s, SendingBuffer);
                    }
                    Send(s, byteF);
    }接受端:
    收到结束标志后进行处理.
    但现在的问题是虽然我会发送一个结束标志“Send(s, byteF); ”但这句根本得不到执行。