EggBender
EggBender

Reputation: 1030

TCP Socket recieves data wrong in C#

I'm trying to send files from a Socket to another Socket. The sockets are running in two different applications, a client application and a server application. This is happening on the same machine now when testing it. The client first sends to the server when it is ready to receive first info about the file that will be sent to it like filename and file size in bytes in a 2056 bytes message. Then it sends a message each time it is ready to receive a new 2048 buffer of file content.

In short, this is the communication flow for each file:

client -"CLIENT_READY_TO_RECEIVE_FILE_INFO"-------> server
client <-"File name+file size of file coming next"-  server
client -"CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER" -> server
client <-"2048 byte buffer of file content"-------- server
...the same flow is repeated until all files are sent to the client.

My problem is that the client receives the file info message wrong, although I have double checked that the server sends it corretly. They both use ASCII encoding. Here is my code:

Client code (receives the files):

private void fetchFilesThreadMethod()
    {
        

        String localHostName = Dns.GetHostName();
        IPAddress[] localIPs = Dns.GetHostAddresses(localHostName);
        IPAddress localIP = localIPs[2];
        int port = 1305;
        IPEndPoint localEP = new IPEndPoint(localIP,port);
        fetchFilesSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        fetchFilesSocket.Bind(localEP);
        fetchFilesSocket.Listen(1);
        fetchFilesSocket = fetchFilesSocket.Accept();

        while (true)
        {
            byte[] receivedFileInfo = new byte[2056];
            byte[] receivedFileCont = new byte[2048];
            byte[] comMessage = new byte[100];
            byte[] comMessageBytes;
            String fileInfoStr = String.Empty;
            String fileNameStr = String.Empty; ;
            String fileExtStr = String.Empty;
            String comMessageStr = String.Empty;
            String fileSizeStr = String.Empty;
            ulong fileSize = 0;
            ulong lastBytesSize = 0;

            comMessageStr = "CLIENT_READY_TO_RECEIVE_FILE_INFO";
            comMessageBytes = Encoding.ASCII.GetBytes(comMessageStr);
            for (int i = 0; i < comMessageBytes.Length; i++)
                comMessage[i] = comMessageBytes[i];
            Console.WriteLine("Bytes available to be flushed by client: " + fetchFilesSocket.Available);
            if (fetchFilesSocket.Available > 0)
            {
                Console.WriteLine(fetchFilesSocket.Available + " bytes flushed by client.");
                byte[] flusher = new byte[fetchFilesSocket.Available];
                fetchFilesSocket.Receive(flusher, 0, fetchFilesSocket.Available, SocketFlags.None);
            }
            fetchFilesSocket.Send(comMessage, 0, 100, SocketFlags.None);
            Console.WriteLine("Client sent ready to receive file info.");
            fetchFilesSocket.Receive(receivedFileInfo,0,2056, SocketFlags.None);
            fileInfoStr = Encoding.ASCII.GetString(receivedFileInfo);
            Console.WriteLine("Received file info:" + fileInfoStr);
            fileNameStr = fileInfoStr.Split(new String[] { "ENDN" }, StringSplitOptions.None)[0];
            Console.WriteLine("Received file name:" + fileNameStr);
            fileExtStr = fileNameStr.Split('.').Last();
            Console.WriteLine("Received file extension:" + fileExtStr);
            fileSizeStr = fileInfoStr.Split(new String[] {"ENDS"},StringSplitOptions.None)[0];
            fileSizeStr = fileSizeStr.Split(new String[] {"ENDN"},StringSplitOptions.None).Last();
            Console.WriteLine("File size string:" + fileSizeStr);
            fileSize = Convert.ToUInt64(fileSizeStr,10);
            Console.WriteLine("Received file size:" + fileSize);
            lastBytesSize = fileSize % 2048;
            ulong byteCount = 0;
            bool keepReceiving = true;
            ulong buffersReceived = 0;
            while (keepReceiving)
            {
                comMessageStr = "CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER";
                comMessageBytes = Encoding.ASCII.GetBytes(comMessageStr);
                for (int i = 0; i < comMessageBytes.Length; i++)
                    comMessage[i] = comMessageBytes[i];
                fetchFilesSocket.Send(comMessage, 0, 100, SocketFlags.None);
                Console.WriteLine("Console sent ready to receive buffer message.");
                if (fileSize - byteCount >= 2048)
                {
                    fetchFilesSocket.Receive(receivedFileCont, 0, 2048, SocketFlags.None);
                    buffersReceived++;
                    Console.WriteLine("Buffers received:" + buffersReceived);
                    byteCount = byteCount + 2048;
                }
                else
                {
                    fetchFilesSocket.Receive(receivedFileCont, 0, 2048, SocketFlags.None);                        buffersReceived++;
                    byteCount = byteCount + 2048;
                    Console.WriteLine("Buffers received:" + buffersReceived);
                    keepReceiving = false;
                }
                Console.WriteLine("Bytes received " + byteCount + "/" + fileSize);
                //Console.WriteLine("Received bytes in current file:" + byteCount);
            }
            Console.WriteLine("File received.");
        }
    }

Server code (sends the files):

private void fetchThreadMethod(Object commandArgs)
    {
        if (fetchSocket == null)
        {
            fetchSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            int port = 1304;    
            IPEndPoint fetchSocketEP = new IPEndPoint(localIP,port);
            fetchSocket.Bind(fetchSocketEP);
        }
        if (!fetchSocket.Connected)
            try
            {
                fetchSocket.Connect(remoteIP, 1305);
                if (fetchSocket.Connected)
                    Console.WriteLine("Server fetch socket connected.");
            }catch(Exception e)
            {
                Console.WriteLine("Something went wrong when connecting the server fetch socket.");
            }
        FileCollector fCollector = new FileCollector();
        String userName = Environment.GetEnvironmentVariable("USERPROFILE");
        String targetDirectory = userName + "\\" + commandArgs;
        Console.WriteLine("Path sent to file collector:" + targetDirectory);
        fCollector.startFileCollector(targetDirectory);
        List<FileNode> collectedFiles = fCollector.getCollectedFiles();
        String comMessageStr = String.Empty;
        foreach (FileNode fNode in collectedFiles)
        {
            comMessageStr = String.Empty;
            byte[] sentFileInfo = new byte[2056];
            byte[] sentFileCont = new byte[2048];
            byte[] comMessage = new byte[100];
            String fileName = fNode.getFileName();
            String formattedFileInfo = fileName;
            formattedFileInfo += "ENDN";
            ulong fileSize = 0;
            FileStream fStream = null;
            try
            {
                fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                FileInfo fInfo = new FileInfo(fileName);
                fileSize = (ulong) fInfo.Length;
                if (fileSize == 0)
                    continue;
                formattedFileInfo += fileSize.ToString() + "ENDS";
            }
            catch (Exception e)
            {
                Console.WriteLine("Could not read from file:" + fileName);
                deniedAccessFiles.Add(fileName);
                continue;
            }
            byte[] fileInfoBytes = Encoding.ASCII.GetBytes(formattedFileInfo);
            for (int i = 0; i < fileInfoBytes.Length; i++)
                sentFileInfo[i] = fileInfoBytes[i];
            while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_FILE_INFO"))
            {
                Console.WriteLine("Server waiting for file info ready message from client.");
                fetchSocket.Receive(comMessage,0,100,SocketFlags.None);
                comMessageStr = Encoding.ASCII.GetString(comMessage);
                comMessageStr = comMessageStr.Substring(0,33);
                Console.WriteLine("Received parsed message from client:" + comMessageStr);
            }
            Console.WriteLine("Server received file info ready message from client.");
            comMessageStr = String.Empty;
            Console.WriteLine("formattedFileInfo:" + formattedFileInfo);
            Console.WriteLine("Sent file info:" + Encoding.ASCII.GetString(sentFileInfo));
            fetchSocket.Send(sentFileInfo, 0, 2056, SocketFlags.None);
            int readByte = 0;
            ulong byteCount = 0;
            ulong buffersSent = 0;
            while (readByte != -1)
            {
                if (byteCount == 2048)
                {
                    while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER"))
                    {
                        Console.WriteLine("Server waiting for ready for buffer message from client.");
                        fetchSocket.Receive(comMessage, 100, SocketFlags.None);
                        comMessageStr = Encoding.ASCII.GetString(comMessage);
                        comMessageStr = comMessageStr.Substring(0,38);
                        Console.WriteLine("Received parsed message from client 1:" + comMessageStr);
                    }
                    Console.WriteLine("Server received ready for buffer message from client.");
                    fetchSocket.Send(sentFileCont, 0, 2048, SocketFlags.None);
                    comMessageStr = String.Empty;
                    buffersSent++;
                    Console.WriteLine("Buffers sent:" + buffersSent);
                    byteCount = 0;
                }
                else
                {
                    readByte = fStream.ReadByte();
                    if (readByte != -1)
                    {
                        sentFileCont[byteCount] = Convert.ToByte(readByte);
                        byteCount++;
                    }
                }   
            }
            while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER"))
            {
                Console.WriteLine("Server waiting for ready for buffer message from client.");
                fetchSocket.Receive(comMessage, 100, SocketFlags.None);
                comMessageStr = Encoding.ASCII.GetString(comMessage);
                comMessageStr = comMessageStr.Substring(0, 38);
                Console.WriteLine("Received parsed message from client 2:" + comMessageStr);
            }
            Console.WriteLine("Server received ready for buffer message from client.");
            fetchSocket.Send(sentFileCont, 0, 2048, SocketFlags.None);
            buffersSent++;
            Console.WriteLine("Buffers sent:" + buffersSent);
            comMessageStr = String.Empty;  
        }
    }

Console outputs: enter image description here

Upvotes: 0

Views: 1724

Answers (1)

ScottishTapWater
ScottishTapWater

Reputation: 4846

My recommendation would be to utilise the TCPListener and TCPClient classes provided within System.Net.Sockets they really simplify sending messages over TCP/IP.

Server side you need something like this:

  class MyTcpListener
    {
        public static void Listen()
        {
            TcpListener server = null;
            byte[] bytes = new byte[256];
            try
            {
                // Set the TcpListener on port 13000.
                const int port = 13000;
                IPAddress localAddr = IPAddress.Parse("127.0.0.1");
                // TcpListener server = new TcpListener(port);
                server = new TcpListener(localAddr, port);
                // Start listening for client requests.
                server.Start();
                // Enter the listening loop.
                while (true)
                {
                    Console.Write("Waiting for a connection... ");
                    // Perform a blocking call to accept requests.
                    // You could also user server.AcceptSocket() here.
                    TcpClient client = server.AcceptTcpClient();
                    Console.WriteLine("Connected!");
                    // Get a stream object for reading and writing
                    NetworkStream stream = client.GetStream();
                    int i;
                    // Loop to receive all the data sent by the client.
                    while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        // Translate data bytes to a ASCII string.
                        string data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                        Console.WriteLine($"Received: {data}");
                        // Process the data sent by the client.
                        data = data.ToUpper();
                        byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
                        // Send back a response.
                        stream.Write(msg, 0, msg.Length);
                        Console.WriteLine($"Sent: {data}");
                    }
                    // Shutdown and end connection
                    client.Close();
                }
            }
            catch (SocketException e)
            {
                Console.WriteLine($"SocketException: {e}");
            }
            finally
            {
                // Stop listening for new clients.
                server?.Stop();
            }
        }
    }

Obviously you'll want to adapt it to your program's structure. I took this from here but edited it slightly.

And on your client side you want to use something like this:

    class MyTCPClient
    {
        static void Connect(String server, String message)
        {
            try
            {
                // Create a TcpClient.
                // Note, for this client to work you need to have a TcpServer 
                // connected to the same address as specified by the server, port
                // combination.
                int port = 13000;
                TcpClient client = new TcpClient(server, port);

                // Translate the passed message into ASCII and store it as a Byte array. Any encoding can be used as long as it's consistent with the server.
                byte[] data = System.Text.Encoding.ASCII.GetBytes(message);

                // Get a client stream for reading and writing.
                //  Stream stream = client.GetStream();
                NetworkStream stream = client.GetStream();

                // Send the message to the connected TcpServer. 
                stream.Write(data, 0, data.Length);
                Console.WriteLine($"Sent: {message}");

                // Receive the TcpServer.response. This is all optional and can be removed if you aren't recieving a response.
                // Buffer to store the response bytes.
                data = new byte[256];

                // String to store the response ASCII representation.

                // Read the first batch of the TcpServer response bytes.
                int bytes = stream.Read(data, 0, data.Length);
                string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
                Console.WriteLine("Received: {responseData}");

                // Close everything.
                stream?.Close();
                client?.Close();
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine($"ArgumentNullException: {e}");
            }
            catch (SocketException e)
            {
                Console.WriteLine($"SocketException: {e}");
            }

            Console.WriteLine("\n Press Enter to continue...");
            Console.Read();
        }
    }

}

Again, this needs to be adapted to your structure and I took it from here. The response stuff can all be removed if you aren't expecting a response from your server.

These two classes are extremely resilient and abstract away all of the complicated stuff, the size of the data buffers can also be changed to whatever size you need.

Upvotes: 1

Related Questions