0

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

5
  • Can I recommend you take a look at TCPClient and TCPServer? They make doing this sort of thing a lot easier Commented Apr 8, 2018 at 13:55
  • I will take a look. Thanks. Commented Apr 8, 2018 at 13:56
  • Why are you manually chunking this up? The underlying socket stream does that for you. 2048 bytes is larger than a single packet anyway. Commented Apr 8, 2018 at 14:05
  • TCP breaks messages into datagrams which have max size of 1500 bytes. the receiver end of a message must know where the message ends and may need to make multiple reads to get all the data. So normally sender does one of following 1) ASCII : terminates data with know character not in message like a return 2) ASCII or Binary : Add a byte count to beginning of message 3) ASCII or Binary : Use fix size messages. Commented Apr 8, 2018 at 14:06
  • 1
    I meant TCPListener in my above comment, not TCPServer take a look at my answer for examples Commented Apr 8, 2018 at 14:21

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I will take a look at TCPClient and TCPListener. James Hughes mentioned TCPServer also. Thanks a lot.
@EggBender that was a typo on my part... TCPServer in my comment referred to TCPListener
Ok I see. TCPClient and TCPListener then it is.
Let me know if you need any help implementing this, I'm around all day

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.