【问题标题】:socket communication works on the same machine but not between different machines套接字通信在同一台机器上工作,但不在不同机器之间
【发布时间】:2017-05-27 22:36:31
【问题描述】:

我编写了一个客户端/服务器应用程序来发送文件。如果服务器和客户端在同一台机器上,它可以工作,但是当我将服务器放在另一台机器上时,服务器读取套接字时会出错。

这是服务器的代码:

class conexion
{
    int sizeofonpacket = 9999;
    string filsc="";
    string titre = "";
    bool sendfilcomand = false;
    int conteur1 = 0;
    int conteur2 = 0;
    BinaryWriter sf;
    TcpListener listiner;
    TcpClient client;
    NetworkStream netStream;

    public conexion(IPAddress ip, int port)
    {
        listiner = new TcpListener(ip, port);
        listiner.Start();
        client = listiner.AcceptTcpClient();
        netStream = client.GetStream();
        Console.Write("client is present \r\n ");
    }

    public void read()
    {
        while (client.Connected)
        {
            string returndata;
            int size = 0;
            string c = "";
            byte[] bs = new byte[4];
            byte[] b = new byte[1];

            try
            {
                //read the comand of client "s" for string or "b" for binary file if it is "s" it read the string that client write
                //if it is "b" we read the string "dfgjsdgfjdsgfjhdsgfj" it is not important 
                Console.Write("ready \r\n ");
                netStream.Read(b, 0, 1);

                c = Encoding.UTF8.GetString(b);
                Console.WriteLine("\r\n comand  :" + c);
                b = new byte[4];
                netStream.Read(b, 0, 4);
                returndata = Encoding.UTF8.GetString(b);
                size = Int32.Parse(returndata);
                Console.WriteLine("\r\n size de packet int =" + size);
                b = new byte[size];
                netStream.Read(b, 0, size);
            }
            catch
            {
                Console.WriteLine("\r\n conexion echoue");
                listiner.Stop();
            }

            switch (c)
            {
                case "b":
                    if (sendfilcomand == false) //if sendfilcomand is false we read first the title
                    {
                        sendfilcomand = true;

                        break;
                    }
                    sendfilcomand = false;
                    filsc = titre;
                    Console.WriteLine("\r\nle titr est:" + titre);

                    titre = "";
                    sf = new BinaryWriter(new FileStream(filsc, FileMode.Create));

                    conteur2 = 0;
                    conteur1 = size;
                    crebfile(b);
                    Console.WriteLine("\r\n creat file for " + conteur2 + " to " + conteur1);
                    b = new byte[sizeofonpacket];
                    while (size != 0)
                    {
                        try
                        {
                            netStream.Read(bs, 0, 4);

                            returndata = Encoding.UTF8.GetString(bs);
                            size = Int32.Parse(returndata);

                            conteur1 = size;
                            Console.WriteLine("sizee a get" + size);

                            if (size == 0)
                            {
                                Console.WriteLine("yout est termine");
                                sf.Close();
                                conteur1 = 0;
                                conteur2 = 0;
                                break;
                            }
                            else if (size != sizeofonpacket)
                            {
                                b = new byte[size];
                            }
                            netStream.Read(b, 0, size);
                        }
                        catch 
                        {
                            Console.WriteLine("\r\n imposible to read ");
                        }

                        crebfile(b);
                        b.Initialize();
                    }

                    sf.Close();
                    conteur1 = 0;
                    conteur2 = 0;
                    break;

                case "s":
                    returndata = Encoding.UTF8.GetString(b);
                    Console.WriteLine("\r\n" + returndata);

                    if (sendfilcomand)
                    {
                        titre = returndata;
                        Console.WriteLine("titre a get" + titre);

                        break;
                    }
                    break;

                default:
                    Console.WriteLine("\r\n rien comand");
                    break;
            }
        }
    }

    public string quadripl(string s)
    {
        while (s.Length < 4)
        {
            s = "0" + s;
        }
        return s;
    }

    public void crebfile(byte[] byts)
    {
        try
        {
            sf.Write(byts, 0, conteur1);
        }
        catch
        {
            Console.WriteLine("imposible de crer le fichier");
        }
    }
}

这是客户端代码:

class conexion
{
    string filsr;
    int sizeofonpacket = 9999;
    bool sendfilcomand = false;
    bool getfilcomand = false;
    int bali;
    int fali;
    Stream file;
    TcpListener listiner;
    TcpClient client;
    NetworkStream netStream;

    public conexion(string ip, int port)
    {
        listiner = null;
        Console.Write("star client ");
        client = new TcpClient(ip, 3568);
        netStream = client.GetStream();
    }

    public void send()
    {
        while (client.Connected)
        {
            //enter the comand "s" or "b"
            string c = "";
            Console.WriteLine("\r\n ecrir comand:");
            c = Console.ReadLine();

            string s = "";
            if (c == "s")
            {
                Console.WriteLine("\r\n entrer string:");
                s = Console.ReadLine();
            }
            string size = "";
            Byte[] sendBytes = null;
            //try
            {
                switch (c)
                {
                    case "b":
                        Console.WriteLine("\r\n comand binary file");
                        netStream.Write(Encoding.UTF8.GetBytes(c), 0, 1);
                        if (sendfilcomand == false) //we will first send the patsh of file after we will send data of file
                        {
                            sendBytes = Encoding.UTF8.GetBytes("dfgjsdgfjdsgfjhdsgfj"); //this is not important
                            size = quadripl(sendBytes.Length.ToString());
                            netStream.Write(Encoding.UTF8.GetBytes(size), 0, Encoding.UTF8.GetBytes(size).Length);
                            netStream.Write(sendBytes, 0, sendBytes.Length);
                            sendfilcomand = true;
                            s = getitr();
                            c = "s";
                            goto case "s";
                        }
                        sendfilcomand = false; //now we will send data
                        filsr = actitr(); //the title is save in "C:/Users/Ce-Pc/Desktop/titreactuel.txt"
                        file = new FileStream(filsr, FileMode.Open);
                        fali = (int)file.Length;
                        bali = 0;
                        byte[] bs = new byte[4];
                        Console.WriteLine("\r\n star sending ");
                        do
                        {

                            sendBytes = filebtobyte(filsr); //read part of file to send
                            Console.WriteLine("\r\n terminer " + bali + " " + " " + fali);
                            size = quadripl(sendBytes.Length.ToString()); //just for add the zero
                            Console.WriteLine("\r\n le size de fichier binair est " + size);
                            netStream.Write(Encoding.UTF8.GetBytes(size), 0, Encoding.UTF8.GetBytes(size).Length);

                            netStream.Write(sendBytes, 0, sendBytes.Length);

                        } while (bali != -1); //when we come to last part of the file (filebtobyte give -1 to bali)
                        bali = 0;
                        size = quadripl("0");
                        Console.WriteLine("\r\n terminer fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiin ");
                        netStream.Write(Encoding.UTF8.GetBytes(size), 0, Encoding.UTF8.GetBytes(size).Length);
                        netStream.Read(bs, 0, 4);
                        break;
                    case "s":
                        Console.WriteLine("\r\n comand string");
                        netStream.Write(Encoding.UTF8.GetBytes(c), 0, 1);
                        size = quadripl(s.Length.ToString());
                        sendBytes = Encoding.UTF8.GetBytes(size);
                        Console.WriteLine("\r\n size=" + size);
                        netStream.Write(sendBytes, 0, sendBytes.Length);
                        sendBytes = Encoding.UTF8.GetBytes(s);
                        netStream.Write(sendBytes, 0, sendBytes.Length);

                        if (sendfilcomand)
                        {
                            c = "b";
                            goto case "b";
                        }
                        break;
                    default:
                        Console.WriteLine("\r\n rien comand");
                        break;
                }
            }
            catch
            {
                Console.WriteLine("\r\n imposible de transfer");
            }
        }
        Console.Write("client est deconect \r\n ");
    }

 //////////////////the functions//////////////////////////////////

    public byte[] filebtobyte(string s)
    {
        byte[] byts = null;
        try
        {
            if (fali - bali < sizeofonpacket)
            {
                byts = new byte[fali - bali];
                file.Read(byts, 0, fali - bali);
                file.Close();
                bali = -1;
            }
            else
            {
                byts = new byte[sizeofonpacket];
                file.Read(byts, 0, sizeofonpacket);
                bali += sizeofonpacket;
            }
        }
        catch
        {
            Console.WriteLine("imposible de trouver le fichier");
        }
        return byts;
    }
    public string quadripl(string s)
    {
        while (s.Length < 4)
        { s = "0" + s; }
        return s;
    }

    public string getitr()
    {
        StreamReader titrfil = new StreamReader("C:/Users/Ce-Pc/Desktop/titre.txt");
        string sss = "";
        try
        {
            sss = titrfil.ReadLine();
            Console.WriteLine("\r\n le chemin " + sss);
            titrfil.Close();
        }
        catch
        {
            Console.WriteLine("\r\n imposible");
        }
        return sss;
    }
    public string actitr()
    {
        StreamReader titrfil = new StreamReader("C:/Users/Ce-Pc/Desktop/titreactuel.txt");
        string sss = "";
        try
        {
            sss = titrfil.ReadLine();
            Console.WriteLine("\r\n le chemin " + sss);
            titrfil.Close();
        }
        catch
        {
            Console.WriteLine("\r\n imposible");
        }
        return sss;
    }
}

【问题讨论】:

  • This 答案可能会有所启发。请注意,NetworkStream.Read 返回读取的字节数。你似乎不在乎,只是假设你会得到你想要的。当您在不同的机器上运行客户端和服务器时,它们之间会存在一个真实的网络,这往往会导致大型传输碎片化,这在一个仅对缓冲区进行洗牌的单个系统上运行可能并不明显。
  • 谢谢你的回答,但我认为 tcp 协议保证发送信息
  • 当你使用netStream.Read(b, 0, 4);没有得到返回值时,你怎么知道读取了四个字节?这对于netStream.Read(b, 0, size); 变得更加成问题。当大小超过大约 1500 字节的典型以太网帧大小时,就会出现碎片。尝试将数据包的大小限制为更小的东西,例如1200 字节。然后获取WireShark 的副本,看看机器之间的真正移动。
  • 我尝试使用等于 1400 的大小但不成功,他在循环的某个步骤中发送了错误数据

标签: c#


【解决方案1】:

您是否检查了两台机器之间的防火墙?

在客户端 PC 上的命令提示符下尝试 telnet 测试以连接到服务器 PC 上的端口:

telnet <server ip> 3568

如果失败,则说明网络级别存在问题(可能与防火墙有关)。

另外,请仔细检查您为客户端和服务器使用相同的端口(您没有在上面的代码中显示服务器端口)

【讨论】:

  • 不,我不认为它是防火墙,因为发送字符串是工作的,smalle fille 可以发送,但如果有一个 big fille,它不会发送文件的所有数据
【解决方案2】:

你的代码被剪切了,我不知道你输入了什么,所以我无法准确预测答案。以下是 4 个可能的答案:

请阅读全部答案

  • 防火墙可能会阻止连接(正如 Ryan 所说)。您可以在控制面板中禁用它
  • 确保您输入了正确的服务器地址以启动 TCP 侦听器(而不是本地默认 ip:127.0.0.1)。要找到正确的 IP 地址,请打开 cmd,在名为“无线 LAN 适配器 Wi-Fi:”的部分中键入“ipconfig”,复制 Ipv4 地址。对于客户端,您必须输入从其他机器生成的 IPv4 地址。 IPv4 以黄色突出显示。

  • 如果两种解决方案都不起作用,那么我必须问您它们是否在同一个 Wi-fi 中。如果不是,那么(当然)如果你是从互联网上的实时服务器而不是路由器中运行它,它就不会工作。

  • 如果不是客户端/服务器中的错误,那么它必须与您的代码一起发送/接收。通过互联网发送 byte[] 并不能保证对方会全部接收(它被分成小数据包),您的代码必须知道何时开始接收以及何时停止接收。您可以在数据的开头添加一个小标题,用于说明文件的长度以及还有多少字节要读取。


更新:

您希望接收完整的数据,而不是小块的数据。所以我创建了一些函数来帮助您更轻松地发送和接收数据:

static void sendMessage(string c_1)
    {
        byte[] command = Encoding.ASCII.GetBytes(c_1);
        byte[] commandLength = Encoding.ASCII.GetBytes(command.Length.ToString());
        List<byte> commandLengthInByteArray = commandLength.ToList();
        while (true)
        {
            if (commandLengthInByteArray.ToArray().Length < 50)
            {
                commandLengthInByteArray.Add(Convert.ToByte('x'));
            }
            else
            {
                break;
            }
        }
        byte[] parsedCommandLength = commandLengthInByteArray.ToArray();
        sck.Send(parsedCommandLength);
        sck.Send(command);
    }
    static void sendMessage_ftp(byte[] data)
    {
        byte[] commandLength = Encoding.ASCII.GetBytes(data.Length.ToString());
        List<byte> commandLengthInByteArray = commandLength.ToList();
        while (true)
        {
            if (commandLengthInByteArray.ToArray().Length < 50)
            {
                commandLengthInByteArray.Add(Convert.ToByte('x'));
            }
            else
            {
                break;
            }
        }
        byte[] parsedCommandLength = commandLengthInByteArray.ToArray();
        sck.Send(parsedCommandLength);
        sck.Send(data);
    }

static byte[] getResponse(Socket sck)
    {
        byte[] CommandLengthInByteArray = ReadBytes(50, sck);
        List<byte> commandLengthInByteArrayList = new List<byte>();
        foreach (byte b in CommandLengthInByteArray)
        {
            if (b.ToString() != "120")
            {
                commandLengthInByteArrayList.Add(b);
            }
        }
        Int32 fileSize = Convert.ToInt32(Encoding.ASCII.GetString(commandLengthInByteArrayList.ToArray()));
        byte[] data = ReadBytes(fileSize, sck);
        return data;
    }
    static byte[] ReadBytes(Int32 size, Socket sck)
    {
        //The size of the amount of bytes you want to recieve, eg 1024
        var bytes = new byte[size];
        Int32 total = 0;
        do
        {
            var read = sck.Receive(bytes, total, size - Convert.ToInt32(total), SocketFlags.None);
            if (read == 0)
            {
                //If it gets here and you received 0 bytes it means that the Socket has Disconnected gracefully (without throwing exception) so you will need to handle that here
            }
            total += read;
            //If you have sent 1024 bytes and Receive only 512 then it wil continue to recieve in the correct index thus when total is equal to 1024 you will have recieved all the bytes
        } while (total != size);
        return bytes;
    }

使用方法:

  • 发送短信:sendMessage("SomeTextHere");
  • 发送文件 (>1 GB) sendMessage_ftp(File.ReadAllBytes(@"C:\Users\StackExchange\Desktop\Image.png"));

  • 接收整个文件或文本(不是我的部分):

    byte[] messageOrFile = getResponse(sck);


但是……

您将不得不放弃使用TcpClient,而是使用System.Net.Sockets

您必须:

  • TcpClient client;替换为Socket sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

  • client = listiner.AcceptTcpClient();替换为client = listiner.AcceptSocket();

【讨论】:

  • 设置服务器端的IP也很重要——可以是0.0.0.0也可以是自己的IP地址(和客户端程序输入的一样),不要设置为127.0。 0.1 或客户端计算机的地址。
  • 这不是防火墙或 IP 错误,客户端和服务器已连接,我的代码知道何时开始接收以及何时停止接收,因为即使是大填充 (50 mb) 也可以发送在同一台机器上,客户端可以发送字符串和小填充问题是当我发送文件的大数据时,在这种情况下,数据没有正确发送
  • @MadaraItachi 我看到了你的问题。再看看我的解决方案!
  • 好的,非常感谢您的回答,如果它有效,我会尝试这个解决方案,即使我不知道我的代码有什么问题,再次感谢您花时间写下所有这些
  • @MadaraItachi 谢谢!我想看看结果。我会等待结果
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多