What is the preferred way to send a list of strings from a python server to a C# client using TCP/sockets?
I've come across tangentially related sources but so far, nothing that specifically addresses this issue (judging by my limited background in networking).
A simple example would be much appreciated!
Thanks!
CodePudding user response:
Use JSON: ["string 1", "string 2", "string 3"]
If your Python server can use an HTTP protocol (for which there are multiple implementations) you can simply use any of the HTTP clients built into .NET. Here's one example:
using System.Text.Json;
using var client = new HttpClient();
var content = await client.GetStringAsync("http://your.server/url");
Console.WriteLine(content);
var stringArray = JsonSerializer.Deserialize<string[]>(content);
CodePudding user response:
There are some ways to achieve this goal. When using socket connections you have to handle data like bytes. So, unlike a string handling (you have a null byte identifying the end of the char array), you don't know where is the end of the byte array. Because of that is a good practice to stablish buffer sizes and identify the end of the strings.
One solution, without error treatment to simplify the reading:
Python Client:
import socket
host = "127.0.0.1"
port = 8080
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
dest = (host, port)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 256) #256 -> expected packet length
print("Connecting ...")
sock.connect(dest)
print("OK")
list = ["first string", "second string", "third string"]
for i in range(len(list)):
sock.send(str.encode(list[i] "$"))
print("Sending " list[i])
sock.close()
C# Tcp Listener:
using System.Net.Sockets;
using System.Text;
TcpListener listener = new TcpListener(System.Net.IPAddress.Any, 8080);
listener.Start();
Console.WriteLine("Waiting for TCP connection ...");
TcpClient tcp = listener.AcceptTcpClient();
tcp.ReceiveBufferSize = 256; //Expected packet length in bytes
Console.WriteLine("Receiving string list ...");
int received_bytes_count = -1;
byte[] total_buffer = new byte[1024]; //Expected total bytes in a string list
while(received_bytes_count != 0)
{
int total_buffer_index = received_bytes_count 1;
NetworkStream ns = tcp.GetStream();
byte[] buffer = new byte[tcp.ReceiveBufferSize];
received_bytes_count = ns.Read(buffer, 0, buffer.Length);
buffer.CopyTo(total_buffer, total_buffer_index);
}
List<string> list = new List<string>();
list.AddRange(Encoding.ASCII.GetString(total_buffer).Split('$'));
list.RemoveAt(list.Count - 1); //remove the null string after the last '$'
tcp.Close();
foreach (string s in list.ToArray())
Console.WriteLine(s);
In this example I fixed 256 bytes to the packet size and 1024 bytes to the string list size. I've used $
to identify end of each string.
In C# Tcp Listener I've used the Encoding.ASCII.GetString()
to convert bytes array into string, and Split()
function to divide the total string received in a List of strings. I remove the last string (after the last $
) because it is the 0 bytes sequence (end of the buffer).
Note that Sockets are handled by the OS, so it doesn't mean that each send()
or Read()
function represents a network packet. So it's very important to use buffer sizes options. Exemplifying a bit more, there's the Nagle's Algorithm designed to reduce network traffic, the OS wait a little bit to gather data that an application is sending with send()
functions and so sends one packet with all the data together, instead of sending small packets that will increase network traffic with more control data. You can turn off the Nagle's Algorithm with setsockopt()
to increase performance, but I think it's not the case for the application that you purpose. It's only to understand that one send()
function doesn't mean one packet, so doesn't mean that one server's Read()
function will receive data of only one client's send()
function. For more details about Nagle's Algorithm:
https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.nodelay?view=net-6.0