小弟最近在做一个语音上传的winform系统,就是在客户端把语音以及一些相关信息上传给服务器端,服务器端在相应的目录保存语音文件,并将有关语音相关的信息写入数据库。日后客户端通过数据库检索出语音并播放,语音都保存在服务器端,不再客户端。开发环境是VS2008 + .Net Framework 3.5 + SQL Server 2005,语音以及信息的上传用的是Socket套接字技术。现在遇到了个让我很头疼的问题,是服务器端的。在服务器端点击开始接收的按钮后,启动下面的线程private void btnStart_Click(object sender, EventArgs e)
        {
            //开启接收线程   
            Thread rec = new Thread(new ThreadStart(this.StartReceive));
            rec.IsBackground = true;
            rec.Start();
        }
//由于既需要传送语音文件,也需要传送字段,所以我定义了几个套接字,侦听不同的端口。
Socket ClientSocket;   //侦听2009端口,是否有语音文件传输。
Socket ClientSocket1;  //侦听2010端口,是否有语音文件的信息传输
Socket ClientSocket2;  //侦听2011端口,是否有客户端播放语音的请求。public void StartReceive()
        {
            //创建一个网络端点   
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 2009);
            //创建一个套接字   
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定套接字到端口   
            server.Bind(ipep);
            //开始侦听(并堵塞该线程)   
            server.Listen(10);            //创建一个网络端点   
            IPEndPoint ipep1 = new IPEndPoint(IPAddress.Any, 2010);
            //创建一个套接字   
            Socket server1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定套接字到端口   
            server1.Bind(ipep1);
            //开始侦听(并堵塞该线程)   
            server1.Listen(10);            ////创建一个网络端点   
            //IPEndPoint ipep2 = new IPEndPoint(IPAddress.Any, 2011);
            ////创建一个套接字   
            //Socket server2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ////绑定套接字到端口   
            //server2.Bind(ipep2);
            ////开始侦听(并堵塞该线程)   
            //server2.Listen(10);
            while (true)
            {
                try
                {
                    Socket client = server.Accept();
                    Socket client1 = server1.Accept();
                    Socket client2 = server2.Accept();
                    ClientSocket = client;
                    ClientSocket1 = client1;
                    ClientSocket2 = client2;
                    //ReceiveFile->接收语音文件的方法
                    Thread recFile = new Thread(new ThreadStart(this.ReceiveFile));
                    recFile.Start();
                    //ReceiveInform->接收语音文件信息并写数据库的方法
                    Thread recInform = new Thread(new ThreadStart(this.ReceiveInform));
                    recInform.Start();
                    //ReceiveRequest->接收客户端播放语音的请求
                    Thread recRequest = new Thread(new ThreadStart(this.ReceiveRequest));
                    recRequest.Start();
                }
                catch (Exception ex)
                {
                    this.listBox1.Items.Add("      " + ex.Message);
                }
            }
        }这里,ReceiveInform与ReceiveRequest这两个方法相似,只是接收字段个数不同。同样的,它们所对应客户端的Send方法也相似。(也许没必要这样分开写)现在的问题是:
 当只有接收语音(ClientSocket)与接收语音信息(ClientSocket1)两个任务的时候,系统一切正常,服务端能接收到客户端发来的语音文件以及语音信息,并将信息写入数据库。
 但一旦加上客户端播放请求(ClientSocket2)这个任务时,服务端接收不到客户端发来的信息了,就连上述两个原本没问题的任务也失效了。
 因为这三个任务单独进行测试的时候是成功的,所以我觉得问题应该不再客户端的Sent方法上。
 后来,我把上述三个任务的套接字都与同一个server绑定,即server.bind(ipep);Socket client = server.accept();ClientSocket = client;3个任务的Receive方法用的套接字都是ClientSocket(同一端口),这个时候服务器就都可以收到客户端发来的信息了,可是区分不了到底是语音文件还是一般的字段信息。
 小弟平时用socket用得也不多,这次和多线程结合一起遇到了不少困难,请大大们帮帮我,经理这两天催得很紧了,这是我全部的分数了,请尽量帮帮。
 可以的话,再帮我看看我这个思路能不能实现我在刚开头所说的系统需求,会不会有什么安全或并发之类的问题,是否需要加锁之类的,谢谢谢谢。

解决方案 »

  1.   

    直接用Socket,够狠建议做WebService或者用Remoting,要不就开个FTP,这样方便些,而且不用为底层的错误担心
      

  2.   


    一个简单想到的方法,既然用了SOCKET,那就补充文件头,写入标识,比如一个简单的01位,来通过个标识位来判断是语音文件,还是字段信息。不知道在你的实现中,这样做是否方便。
      

  3.   


    你不是走协议发送么?万一中途中断怎么办?我以为你是以发包的形式来传送呢。如果是完全的文件流的形式的话,那就以一个结构体的方式来发,结构体内设一个域来标识数据的类型。我的思路以为你是以发包的形式进行文件传输的呢……一般都是这样做,起码的UDP就挺好的了。楼主的是不是以流形式直接传?若是的话……我就没有什么想法了……呵呵……
      

  4.   

    每个socket一个线程
    你这样放一块...还是同步socket会阻赛的
      

  5.   

    如果便是局限网内传输,也尽可能使用UDP吧。只是一个建议……比如可能需要大改,那就不可取了……其实你的问题就是时序上排不开……所以……还是需要用同步来解决……总之,标识位是一定要设的,怎么设,设在哪儿,如果走协议的话就方便了,否则当然没办法进行区分了,我目前没有想出别的好办法……
      

  6.   

    仔细看了一下阁下遇到的问题,其实Socket是个博大精深的东西.有很多细节都需要慢慢专研的.
    在下认为你的问题不是出在SOCKET上,而是在你的三个方法上,建议阁下检查一下ReceiveRequest这个方法是否需要另外两个方法的支持?
    另外,你自己解决办法中提到无法辨别到底是语音文件还是一般的字段信息的问题,其实可以考虑2楼朋友的办法,就是在客户端发送的请求信息上
    加上每种信息的标识,比如以01开头表示语音文件,以02一般的字段信息等等.
      

  7.   

    大家说的都挺有理的,可小弟还是解决不了,在这里把Sent和Receive的方法都发上来,请大家帮我看看吧,谢谢了。首先是发送和接收语音文件
    int.Parse(xml.LoadFile("Port")) //读XML文件,已设成2009
    //发送文件
    public void SendFile(string fileName)
            {
                //创建一个文件对象   
                FileInfo EzoneFile = new FileInfo(fileName);
                //打开文件流   
                FileStream EzoneStream = EzoneFile.OpenRead();
                //包的大小   
                int PacketSize = 40000;
                //包的数量   
                int PacketCount = (int)(EzoneStream.Length / ((long)PacketSize));
                //最后一个包的大小   
                int LastDataPacket = (int)(EzoneStream.Length - ((long)(PacketSize * PacketCount)));
                //指向远程服务端节点   
                IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(xml.LoadFile("ServerAddr")), int.Parse(xml.LoadFile("Port")));
                //创建套接字   
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //连接到发送端   
                client.Connect(ipep);
                //获得客户端节点对象   
                IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
                //发送[文件名]到客户端   
                TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(EzoneFile.Name));
                //发送[包的大小]到客户端   
                TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(PacketSize.ToString()));
                //发送[包的总数量]到客户端   
                TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(PacketCount.ToString()));
                //发送[最后一个包的大小]到客户端   
                TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(LastDataPacket.ToString()));
                //数据包   
                byte[] data = new byte[PacketSize];
                //开始循环发送数据包   
                for (int i = 0; i < PacketCount; i++)
                {
                    //从文件流读取数据并填充数据包   
                    EzoneStream.Read(data, 0, data.Length);
                    //发送数据包   
                    TransferFiles.SendVarData(client, data);
                }            //如果还有多余的数据包,则应该发送完毕!   
                if (LastDataPacket != 0)
                {
                    data = new byte[LastDataPacket];
                    EzoneStream.Read(data, 0, data.Length);
                    TransferFiles.SendVarData(client, data);
                }
                //关闭套接字   
                client.Close();
                //关闭文件流   
                EzoneStream.Close();
            }//接收文件
     public void ReceiveFile()
            {
                Socket client = ClientSocket;
                //确认连接   
                //Socket client = server.Accept();
                //获得客户端节点对象   
                IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
                //获得[文件名]   
                SendFileName = System.Text.Encoding.Unicode.GetString(TransferFiles.ReceiveVarData(client));
                //获得路径 + 文件名
                fullFileName = xml.LoadFile("Path") + "Record\\" + DateTime.Now.ToString("yyyyMMdd") + "\\" + userName + "-" + SendFileName;
                //获得[包的大小]   
                string bagSize = System.Text.Encoding.Unicode.GetString(TransferFiles.ReceiveVarData(client));
                //获得[包的总数量]   
                int bagCount = int.Parse(System.Text.Encoding.Unicode.GetString(TransferFiles.ReceiveVarData(client)));
                //获得[最后一个包的大小]   
                string bagLast = System.Text.Encoding.Unicode.GetString(TransferFiles.ReceiveVarData(client));
                try
                {
                    //创建一个新文件   
                    FileStream myFileStream = new FileStream(fullFileName, FileMode.Create, FileAccess.Write);
                    //已发送包的个数   
                    int SendedCount = 0;
                    while (true)
                    {
                        byte[] data = TransferFiles.ReceiveVarData(client);
                        if (data.Length == 0)
                        {
                            break;
                        }
                        else
                        {
                            SendedCount++;
                            //将接收到的数据包写入到文件流对象   
                            myFileStream.Write(data, 0, data.Length);
                        }
                    }
                    //关闭文件流   
                    myFileStream.Close();
                }
                catch(Exception ex)
                {
                    string inform = ex.Message + "                    " + this.lblTime.Text + "                    " + this.lblDate.Text;
                    this.listBox1.Items.Add(inform);
                }
                //关闭套接字   
                client.Close();
            }接着,发送和接收语音文件信息
    //发送
     public void SendInform(string userName, string number, TimeSpan interval, string type, string clientID, string clientName, string clientPath, string memo)
            {
                IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(xml.LoadFile("ServerAddr")), int.Parse(xml.LoadFile("Port")) + 1);
                //创建套接字   
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //连接到发送端   
                client.Connect(ipep);
                //获得客户端节点对象   
                //IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
                byte[] buffer1 = Encoding.UTF8.GetBytes(userName + "^");
                byte[] buffer2 = Encoding.UTF8.GetBytes(number + "^");
                byte[] buffer3 = Encoding.UTF8.GetBytes(interval.ToString().Substring(0, 8) + "^");
                byte[] buffer4 = Encoding.UTF8.GetBytes(type + "^");
                byte[] buffer5 = Encoding.UTF8.GetBytes(clientID + "^");
                byte[] buffer6 = Encoding.UTF8.GetBytes(clientName + "^");
                byte[] buffer7 = Encoding.UTF8.GetBytes(clientPath + "^");
                byte[] buffer8 = Encoding.UTF8.GetBytes(memo);
                int bytesSent1 = client.Send(buffer1);
                int bytesSent2 = client.Send(buffer2);
                int bytesSent3 = client.Send(buffer3);
                int bytesSent4 = client.Send(buffer4);
                int bytesSent5 = client.Send(buffer5);
                int bytesSent6 = client.Send(buffer6);
                int bytesSent7 = client.Send(buffer7);
                int bytesSent8 = client.Send(buffer8);
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
    //接收
     public void ReceiveInform()
            {
                Socket inform = ClientSocket1;
                string data = "";
                //确认连接    
                //Socket file = server.Accept();
                //获得客户端节点对象   
                //IPEndPoint clientep = (IPEndPoint)file.RemoteEndPoint;
                try
                {
                    while (true)
                    {
                        byte[] bytes = new Byte[1024];
                        int bytesRec = inform.Receive(bytes);
                        data += Encoding.UTF8.GetString(bytes, 0, bytesRec);
                        if (Encoding.UTF8.GetString(bytes, 0, bytesRec) == "")
                        {
                            data = data.Trim();
                            break;
                        }
                    }
                    string[] element = data.Split('^');
                 }
                catch (Exception ex)
                {
                    string information = ex.Message + "    " + this.lblTime.Text + "     " + this.lblDate.Text;
                    this.listBox1.Items.Add(information);
                }
                //关闭套接字
                inform.Close();
            }最后,发送和接收客户端播放请求
    //发送
    public void SendPlayRequest(string recordID) //发送语音文件在数据库中的键与客户端IP 
            {
                IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(xml.LoadFile("ServerAddr")), int.Parse(xml.LoadFile("Port")) + 2);
                //创建套接字   
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //连接到发送端   
                client.Connect(ipep);
                //获得客户端节点对象   
                //IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
                string clientIP = GetIp();
                byte[] buffer1 = Encoding.UTF8.GetBytes(recordID + "^");
                byte[] buffer2 = Encoding.UTF8.GetBytes(clientIP);
                int bytesSent1 = client.Send(buffer1);
                int bytesSent2 = client.Send(buffer2);
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }//接收
    public void ReceiveRequest()
            {
                Socket request = ClientSocket2;
                string data = "";
                //确认连接    
                //Socket file = server.Accept();
                //获得客户端节点对象   
                //IPEndPoint clientep = (IPEndPoint)file.RemoteEndPoint;
                try
                {
                    while (true)
                    {
                        byte[] bytes = new Byte[1024];
                        int bytesRec = request.Receive(bytes);
                        data += Encoding.UTF8.GetString(bytes, 0, bytesRec);
                        if (Encoding.UTF8.GetString(bytes, 0, bytesRec) == "")
                        {
                            data = data.Trim();
                            break;
                        }
                    }
                    string[] element = data.Split('^');
                    SendFile(element[0], element[1]);//这个是从服务器端把语音文件发到客户端以供其播放的方法,前面的问题解决不了根本就没办法                                                       运行这个,请先忽略
                }
                catch (Exception ex)
                {
                    string information = ex.Message + "    " + this.lblTime.Text + "     " + this.lblDate.Text;
                    this.listBox1.Items.Add(information);
                }
                finally
                {
                    //关闭套接字
                    //request.Close();
                }
            }
      

  8.   

    请大家着重帮我看看后面两个任务,应该都是差不多的,上面的朋友说ReceiveRequest可能有问题,请帮我看看,小弟不胜感激。