How to send a file through a socket in C #

I have applications for servers and client consoles that communicate perfectly and also send some string. Here is the code ...

Server

public static void Main() { try { IPAddress ipAd = IPAddress.Parse("127.0.0.1"); /* Initializes the Listener */ TcpListener myList = new TcpListener(ipAd, 1234); /* Start Listeneting at the specified port */ myList.Start(); Console.WriteLine("The server is running at port 8001..."); Console.WriteLine("The local End point is :" + myList.LocalEndpoint); Console.WriteLine("Waiting for a connection....."); Socket s = myList.AcceptSocket(); Console.WriteLine("Connection accepted from " + s.RemoteEndPoint); byte[] b = new byte[100]; int k = s.Receive(b); Console.WriteLine("Recieved..."); for (int i = 0; i < k; i++) Console.Write(Convert.ToChar(b[i])); ASCIIEncoding asen = new ASCIIEncoding(); s.Send(asen.GetBytes("The string was recieved by the server.")); Console.WriteLine("\nSent Acknowledgement"); /* clean up */ s.Close(); myList.Stop(); } catch (Exception e) { Console.WriteLine("Error..... " + e.StackTrace); } } 

Client

 public static void Main() { try { TcpClient tcpclnt = new TcpClient(); Console.WriteLine("Connecting..."); tcpclnt.Connect("127.0.0.1", 1234); Console.WriteLine("Connected"); Console.Write("Enter the string to be transmitted: "); String str = Console.ReadLine(); Stream stm = tcpclnt.GetStream(); ASCIIEncoding asen = new ASCIIEncoding(); byte[] ba = asen.GetBytes(str); Console.WriteLine("Transmitting..."); stm.Write(ba, 0, ba.Length); byte[] bb = new byte[100]; int k = stm.Read(bb, 0, 100); for (int i = 0; i < k; i++) Console.Write(Convert.ToChar(bb[i])); tcpclnt.Close(); } catch (Exception e) { Console.WriteLine("Error... " + e.StackTrace); } } 

Now I need to add a code algorithm that will send the file through the same applications. The key value to implement is to use Socket.Send in the client. I'm not sure about serializing and de-serializing a file.

Any hints, tips, suggestions are more welcome. Thanks.

+4
source share
3 answers

According to the request, you can do the following on the client side - as suggested by this post , except that you may want to change the Send part async Send synchronization as follows:

 clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async 

And your callback might look like this:

 private static void endSendCallback(IAsyncResult ar) { try { SocketError errorCode; int result = clientSocket.EndSend(ar, out errorCode); Console.WriteLine(errorCode == SocketError.Success ? "Successful! The size of the message sent was :" + result.ToString() : "Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one ); } catch (Exception e) { //exception Console.WriteLine("Unhandled EndSend Exception! " + e.ToString()); //do something like retry or just report that the sending fails //But since this is an exception, it probably best NOT to retry } } 

Regarding the full client side code after this:

 using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace TcpClientConsoleApplication { class Program { const int PORT_NO = 2201; const string SERVER_IP = "127.0.0.1"; static Socket clientSocket; //put here static void Main(string[] args) { //Similarly, start defining your client socket as soon as you start. clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); loopConnect(3, 3); //for failure handling string result = ""; do { result = Console.ReadLine(); //you need to change this part if (result.ToLower().Trim() != "exit") { byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async //clientSocket.Send(bytes); use this for sync send } } while (result.ToLower().Trim() != "exit"); } private static void endSendCallback(IAsyncResult ar) { try { SocketError errorCode; int result = clientSocket.EndSend(ar, out errorCode); Console.WriteLine(errorCode == SocketError.Success ? "Successful! The size of the message sent was :" + result.ToString() : "Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one ); } catch (Exception e) { //exception Console.WriteLine("Unhandled EndSend Exception! " + e.ToString()); //do something like retry or just report that the sending fails //But since this is an exception, it probably best NOT to retry } } static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) { int attempts = 0; while (!clientSocket.Connected && attempts < noOfRetry) { try { ++attempts; IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null); result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds)); System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000); } catch (Exception e) { Console.WriteLine("Error: " + e.ToString()); } } if (!clientSocket.Connected) { Console.WriteLine("Connection attempt is unsuccessful!"); return; } } private const int BUFFER_SIZE = 4096; private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message private static void endConnectCallback(IAsyncResult ar) { try { clientSocket.EndConnect(ar); if (clientSocket.Connected) { clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket); } else { Console.WriteLine("End of connection attempt, fail to connect..."); } } catch (Exception e) { Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString()); } } const int MAX_RECEIVE_ATTEMPT = 10; static int receiveAttempt = 0; private static void receiveCallback(IAsyncResult result) { System.Net.Sockets.Socket socket = null; try { socket = (System.Net.Sockets.Socket)result.AsyncState; if (socket.Connected) { int received = socket.EndReceive(result); if (received > 0) { receiveAttempt = 0; byte[] data = new byte[received]; Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET! //Notice that your data is not string! It is actually byte[] //For now I will just print it out Console.WriteLine("Server: " + Encoding.UTF8.GetString(data)); socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again ++receiveAttempt; socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); } else { //completely fails! Console.WriteLine("receiveCallback is failed!"); receiveAttempt = 0; clientSocket.Close(); } } } catch (Exception e) { // this exception will happen when "this" is be disposed... Console.WriteLine("receiveCallback is failed! " + e.ToString()); } } } } 

Full explanation of how the above code snippets work (note that point 7 has been changed and point 8 has been added for async Send ):

Customer:

  • Similarly, put the Socket class in the class context, not in the method context, and initialize it as soon as you run your program

     const int PORT_NO = 2201; const string SERVER_IP = "127.0.0.1"; static Socket clientSocket; //put here static void Main(string[] args) { //Similarly, start defining your client socket as soon as you start. clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //your other main routines } 
  • Then start the connection using ASync BeginConnect . I usually went further than LoopConnect only to handle errors like this.

     static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) { int attempts = 0; while (!clientSocket.Connected && attempts < noOfRetry) { try { ++attempts; IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnect, null); result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds)); System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000); } catch (Exception e) { Console.WriteLine("Error: " + e.ToString()); } } if (!clientSocket.Connected) { Console.WriteLine("Connection attempt is unsuccessful!"); return; } } 
  • A similar concept of what you are doing on the BeginAccept server, you need to define endConnectCallback for the ASync BeginConnect . But here , unlike the server that needs to call BeginAccept , after you connect, you do not need to make any new BeginConnect , since you only need to connect once .

  • You might want to declare buffer , etc. Then, after you connect, do not forget the next ASync BeginReceive process the message extraction part (similar to the server)

     private const int BUFFER_SIZE = 4096; private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message private static void endConnectCallback(IAsyncResult ar) { try { clientSocket.EndConnect(ar); if (clientSocket.Connected) { clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket); } else { Console.WriteLine("End of connection attempt, fail to connect..."); } } catch (Exception e) { Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString()); } } 
  • Naturally, you need to define your receiveCallback , as well as what you did for the server. And yes, this, you guessed it, is almost identical to what you did for the server!

  • You can do whatever you want with your data. Note that the data you received is actually in byte[] , not string . That way you can do anything with her. But , for example, sake , I just use string to display.

     const int MAX_RECEIVE_ATTEMPT = 10; static int receiveAttempt = 0; private static void receiveCallback(IAsyncResult result) { System.Net.Sockets.Socket socket = null; try { socket = (System.Net.Sockets.Socket)result.AsyncState; if (socket.Connected) { int received = socket.EndReceive(result); if (received > 0) { receiveAttempt = 0; byte[] data = new byte[received]; Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //copy the data from your buffer //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET! //Notice that your data is not string! It is actually byte[] //For now I will just print it out Console.WriteLine("Server: " + Encoding.UTF8.GetString(data)); socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again ++receiveAttempt; socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); } else { //completely fails! Console.WriteLine("receiveCallback is failed!"); receiveAttempt = 0; clientSocket.Close(); } } } catch (Exception e) { // this exception will happen when "this" is be disposed... Console.WriteLine("receiveCallback is failed! " + e.ToString()); } } 
  • And the next one (to the very last) - Yes, again, as you may have guessed, you just need to do something in your main routine - suppose you want to use it for BeginSend data. Since you are using Console , but want it to send things like byte[] , you need to do the conversion (see Explanation on server 9 from the linked message ).

     static void Main(string[] args) { //Similarly, start defining your client socket as soon as you start. clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); loopConnect(3, 3); //for failure handling string result = ""; do { result = Console.ReadLine(); //you need to change this part if (result.ToLower().Trim() != "exit") { byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async //clientSocket.Send(bytes); use this for sync send } } while (result.ToLower().Trim() != "exit"); } 
  • And finally, in the very last case, you only need to declare the async EndSend callback function, and you're done!

     private static void endSendCallback(IAsyncResult ar) { try { SocketError errorCode; int result = clientSocket.EndSend(ar, out errorCode); Console.WriteLine(errorCode == SocketError.Success ? "Successful! The size of the message sent was :" + result.ToString() : "Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one ); } catch (Exception e) { //exception Console.WriteLine("Unhandled EndSend Exception! " + e.ToString()); //do something like retry or just report that the sending fails //But since this is an exception, it probably best NOT to retry } } 
+2
source

You just need to read the file as a byte array, and then send this byte array through the wire

 var bytes = File.ReadAllBytes(path); stm.Write(ba, 0, ba.Length); 
+1
source

File.ReadAllBytes (string path) will provide you with an array of file bytes.
File.WriteAllBytes (string, bytes [] bytes) will write it to disk.
To help distinguish between file / command / status / etc. the contents of the message you could add a header byte in front. Enum: A byte can come in handy here.

+1
source

Source: https://habr.com/ru/post/1247342/


All Articles