0

I've been trying to fix this problem for quite a while now and it's a fairly annoying one. Basically, I have a XNA program that connects to a server and downloads pictures from it to use as texture2D. Everything works fine when tested in LAN, but when I send the program to a friend to test it over WAN, the image ends up randomly corrupted. In most cases, the start of the image is there but after that it's just the same color. I've tried all solutions I could find around and nothing seems to fix it. Here's my code for sending/receiving the picture:

Client

        NetworkStream nws = tcpcl.GetStream();
        while (true)
        {
            byte[] buffer = new byte[1024*1024*5];
            int buffpos = 0;
            while (nws.DataAvailable == false) Thread.Sleep(50);
            while (nws.DataAvailable == true)
            {
                buffer[buffpos] = (byte)nws.ReadByte();
                buffpos++;
            }

            byte[] actData = new byte[buffpos];
            for (int I = 0; I < buffpos; I++)
            {
                actData[I] = buffer[I];
            }

            string message = ASCIIEncoding.ASCII.GetString(actData);
            string ID = message.Substring(0, 1);
            string data = message.Substring(1);
            if (actData[0] == 1)
            {
                int namelength = BitConverter.ToInt32(actData, 1);
                byte[] _name= new byte[namelength];
                Array.Copy(actData, 5, _name, 0, namelength);
                string name = ASCIIEncoding.ASCII.GetString(_name);

                int avatarlength = BitConverter.ToInt32(actData, 5 + namelength);
                byte[] avatar = new byte[avatarlength];
                Array.Copy(actData, 9 + namelength, avatar, 0, avatarlength)
                    ;
                File.WriteAllBytes("content/" + name + ".png", avatar);

                FileStream fs=  File.OpenRead("Content/" + name + ".png");
                User _user = new User(name, Texture2D.FromStream(GraphicsDevice, fs));
                fs.Close();

                User.AddUser(_user);
            }
        }

Server

                us.endpoint = new IPEndPoint(groupEP.Address, groupEP.Port);
                us.gameenabled = true;
                us.x = Game.spawnx;
                us.y = Game.spawny;

                byte[] player_avatar = Game.playerAvatar(us.Username);
                byte[] setup= new byte[5+player_avatar.Length];

                setup[0] = (byte)maxcls;
                BitConverter.GetBytes(player_avatar.Length).CopyTo(setup, 1);
                player_avatar.CopyTo(setup, 5);

                NetworkStream nws = us.clientsock.GetStream();
                nws.Write(setup, 0, setup.Length);

Efficiency aside, is there anything in the code that could cause said problem over WAN?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • 1
    Are the two bytes arrays the same? I think what may be happening is some of the bytes are being wrongly interpreted as control characters. The way around this is to use base64 encoding: `Convert.ToBase64String` or `Convert.FromBase64String`. [Click to see why to use Base64](http://stackoverflow.com/questions/201479/what-is-the-use-of-base-64-encoding) – Brownish Monster Jan 19 '13 at 13:00
  • Moreover, I've also encountered a problem with .NET 4.0 client, and .NET 4.0 server (but with .NET 4.5 installed). Not sure if this applies here. Just saying. – SmartK8 Jan 19 '13 at 13:07
  • Hi @João, can you contact me at eminakbulut@gmail.com please? – Nime Cloud Feb 14 '13 at 13:40
  • 1
    @BrownishMonster this helped me fix my problem, sorry I didn't thank you earlier, but thanks a lot! – João Miguel Brandão Oct 29 '14 at 15:23
  • @JoãoMiguelBrandão You're welcome. I would have put it as an answer but my comment was actually just a guess. It's something I read that fixed a problem of mine, my problem wasn't TCP related but I heard they were related. – Brownish Monster Nov 16 '14 at 23:14

2 Answers2

0

BrownishMonster solved the problem in a comment.

Are the two bytes arrays the same? I think what may be happening is some of the bytes are being wrongly interpreted as control characters. The way around this is to use base64 encoding: Convert.ToBase64String or Convert.FromBase64String.

0

After countless hours of research I found the answer.

See...when you write synchronously or asynchronously (especially asynchronously) chunks of binaries that compose a larger file size than, let's say 8000 bytes the binaries can lag behind in a stream and be assembled in the receiving end incorrectly. The solution for this is to wrap the socket object in a NetworkStream and to send the data to the receiving end byte by byte.

I will hear angry nerds telling me that it is inefficient to do this but it is the only way for files to be transferred correctly.

The solution?

Implement a hybrid system for text and files, if you need to send both.





The data transmission end method:

Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);

client.Connect()

NetworkStream network_data_stream = new NetworkStream(client);


///////////////////////////////////
// Text information transmission //
///////////////////////////////////

byte[] text_info = new byte[TEXT_INFO_SIZE];

network_data_stream .Write(text_info, 0, text_info.Lenght);

///////////////////////////////////////////







///////////////////////////////////
// File information transmission //
///////////////////////////////////


List<byte> file_info = List<byte>();


for(int Index = 0; Index <= file_info.Lenght - 1; Index++)
{
   network_data_stream.WriteByte((byte) file_info[Index]);
}


////////////////////////////////////////////////////////////




The receiving end method:


Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);

server.Bind(new IPEndPoint(IPAddress.Any, port));
server.Listen(3000);

Socket client = server.Accept();

NetworkStream network_data_stream = new NetworkStream(client);


///////////////////////////////////
// Text information transmission //
///////////////////////////////////

byte[] text_info = new byte[TEXT_INFO_SIZE];

network_data_stream .Read(text_info, 0, text_info.Lenght);

///////////////////////////////////////////







///////////////////////////////////
// File information transmission //
///////////////////////////////////


List<byte> file_info = List<byte>();


for(int Index = 0; Index <= file_info.Lenght - 1; Index++)
{
   network_data_stream.ReadByte((byte) file_info[Index]);
}

////////////////////////////////////////////////////////////







//////////////////////
// ! ! ! NOTE ! ! ! //
//////////////////////

/*
You will need to modify this methods in order to send the file size to the receiving end before sending the file, in order to set a fixed buffer size for the file.


You can use the WriteAsync method instead of the Write method for text information transmission in order to improve performance, without any binary fragmentation cost if the data to be transmitted is lower than 8000 bytes.


If the text information to be sent is greater than 8000 bytes, the recommended method is to use the byte by byte information transmission method. This method can be used inside SslStream.

*/
teodor mihail
  • 343
  • 3
  • 7