Receiving and processing continous packets using UDP - c#

This is my current setup (using UDP):
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packet
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
}
I was wondering what would happen if I immediately call socket.BeginReceive after calling EndReceive and then process the packet to obtain a continous packet flow like this:
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packets
}
If a packet is received as soon as I call BeginReceive, would this conflict with the current packet processing somehow?
Also if this would not conflict would changing to TCP make this disfunctional?

Looks like you are creating some sort of recursive handler there. I am unsure how that will work, probably not in a good way. I usually go for a separate reader thread that listens to incoming data and passes it on to an event. This has served me well in the past. I have not looked into using async for this though.
Here is some example code on how to use a separate thread to handle incoming UDP data. It is not complete but should give you an idea of how to set it up.
private Thread _udpReadThread;
private volatile bool _terminateThread;
public event DataEventHandler OnDataReceived;
public delegate void DataEventHandler(object sender, DataEventArgs e);
private void CreateUdpReadThread()
{
_udpReadThread = new Thread(UdpReadThread) { Name = "UDP Read thread" };
_udpReadThread.Start(new IPEndPoint(IPAddress.Any, 1234));
}
private void UdpReadThread(object endPoint)
{
var myEndPoint = (EndPoint)endPoint;
var udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// Important to specify a timeout value, otherwise the socket ReceiveFrom()
// will block indefinitely if no packets are received and the thread will never terminate
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100);
udpListener.Bind(myEndPoint);
try
{
while (!_terminateThread)
{
try
{
var buffer = new byte[1024];
var size = udpListener.ReceiveFrom(buffer, ref myEndPoint);
Array.Resize(ref buffer, size);
// Let any consumer(s) handle the data via an event
FireOnDataReceived(((IPEndPoint)(myEndPoint)).Address, buffer);
}
catch (SocketException socketException)
{
// Handle socket errors
}
}
}
finally
{
// Close Socket
udpListener.Shutdown(SocketShutdown.Both);
udpListener.Close();
}
}
public class DataEventArgs : EventArgs
{
public byte[] Data { get; private set; }
public IPAddress IpAddress { get; private set; }
public DataEventArgs(IPAddress ipaddress, byte[] data)
{
IpAddress = ipaddress;
Data = data;
}
}

Related

Confusion with async networking in C#

I wrote a C# game server that usually has several hundred people connected to it, and based it on this example from Microsoft. The players are always sending data back and forth, and the connections are rarely idle.
I have started wondering though: is every player connection launching its own Thread? Would it be better performance-wise if I recoded it to use 'Tasks/Await/etc' instead?
[EDIT]
Is the way I'm calling Thread.Sleep() an appropriate way to throttle someone's connection? Or, instead of using Thread.Sleep(), should I have it launch a task with Task.Delay() that contains SendBytes()
public class Server
{
void Listen()
{
IPAddress IpAddr = IPAddress.Parse(ipv4_local);
IPEndPoint ipEnd = new IPEndPoint(IpAddr, port);
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(ipEnd);
listener.Listen(5000);
while (true)
{
listener_allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptConnection), listener);
listener_allDone.WaitOne();
}
}
void AcceptConnection(IAsyncResult ar)
{
listener_allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
Player player = new Player(handler);
player.InitReceive();
}
}
And the player class, where Thread.Sleep() is used to throttle...
public class Player
{
public Socket socket;
public byte[] send_buffer = new byte[2048];
public byte[] receive_buffer = new byte[2048];
public Player(Socket socket)
{
this.socket = socket;
}
void SendBytes()
{
socket.BeginSend(send_buffer, 0, send_buffer.Length, SocketFlags.None, OnSendComplete, null);
}
void OnSendComplete(System.IAsyncResult iar)
{
int bytes_sent = socket.EndSend(iar);
if (MORE BYTES TO SEND)
{
Thread.Sleep(100); //THROTTLE
SendBytes();
}
}
public void InitReceive()
{
socket.BeginReceive(receive_buffer, 0, receive_buffer.Length, 0, new AsyncCallback(ReadCallback), this);
}
public void ReadCallback(IAsyncResult ar)
{
Player player = (Player)ar.AsyncState;
Socket socket = player.socket;
int bytesRead = socket.EndReceive(ar);
if (bytesRead != 0)
{
// READ BYTES...
// PROCESS DATA...
if (NEED TO SEND RESPONSE)
{
SendBytes();
}
socket.BeginReceive(player.receive_buffer, 0, player.receive_buffer.Length, 0, new AsyncCallback(ReadCallback), player);
}
}
}

CSharp async server program immediately closes at start

im pretty to new to programming and i've just now wrote my first async server, but as soon as i start the program, it closes right after writing waiting for new client, can someone help me out? I dont know what stupid thing im doing.
I have a class UDPServer with some methods, of which
- UDPServer() receives an ipendpoint of the localserver ipaddress and port to initialize stuff (called in main to create a new UDP server object)
- right after that i call the get connection method, which supposedly should make the server wait for a new client, but instead it immediately closes.
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket clientSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
class UDPServer
{
Socket serverSocket;
IPEndPoint localIPEP;
IPEndPoint senderIPEP;
EndPoint sender;
IPEndPoint[,] playerList;
int playerListIndex;
bool waitingForSecondClient;
public UDPServer(IPEndPoint serverIpEndPoint)
{
localIPEP = serverIpEndPoint;
serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
serverSocket.Bind(localIPEP);
senderIPEP = new IPEndPoint(IPAddress.Any, 0);
sender = senderIPEP;
playerList = new IPEndPoint[5000, 2]; // 5000 possible player lobbies, each with 2 player ip addresses and ports
playerListIndex = 0; // we start filling up the lobbies from 0
Console.WriteLine("Server setup complete.");
}
public void GetConnection()
{
StateObject state = new StateObject();
Console.WriteLine("Waiting for new client.");
serverSocket.BeginReceiveFrom(state.buffer, 0, StateObject.BufferSize, SocketFlags.None,ref sender, ClientConnected, state);
}
public void ClientConnected(IAsyncResult asyncResult)
{
StateObject state = (StateObject)asyncResult;
EndPoint remote = state.clientSocket.LocalEndPoint;
StateObject tempState = new StateObject();
int bytesReceived = serverSocket.EndReceiveFrom(asyncResult, ref remote);
serverSocket.BeginReceiveFrom(tempState.buffer, 0, StateObject.BufferSize, SocketFlags.None, ref remote, ClientConnected, tempState);
Console.WriteLine("-------------");
Console.WriteLine("Received bytes of data: " + bytesReceived);
Console.WriteLine("-------------");
Console.WriteLine("Received string: " + state.sb.ToString());
Console.WriteLine("-------------");
if (state.sb.ToString().Equals("New client"))
{
Send(state.clientSocket, "Hello");
}
}
private void Send(Socket client,string message)
{
EndPoint remote = client.LocalEndPoint;
StateObject state = new StateObject();
// Begin sending the data to the remote device.
serverSocket.BeginSendTo(state.buffer, 0, StateObject.BufferSize, 0,remote,SendCallback,state);
}
private static void SendCallback(IAsyncResult asyncResult)
{
// Retrieve the socket from the state object.
Socket client = (Socket)asyncResult.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSendTo(asyncResult);
}
static void Main(string[] args)
{
UDPServer server = new UDPServer(new IPEndPoint(IPAddress.Any, 9050));
server.GetConnection();
}
}
BeginReceiveFrom Does not block, as such program execution reaches the end of your main method and finishes. I am not sure what you intend your exit condition to be, but you will need some sort of code making sure the server waits until that exit condition is met before finishing program execution.

C# TCP Socket callback not returning bytes for first sent message

I have a console app that listens for client socket connections and the packets being sent. Once a packet is sent, it outputs the length of the packet. I am trying to create a bit more organization to my application and keep some of these actions within their own objects. However, I have run into a small issue where the first message comes back as 0, but the rest of them work just fine.
I have an object that handles the data being received:
public class Receiver : IDisposable
{
private byte[] _buffer;
public Receiver(byte[] buffer)
{
this._buffer = buffer;
}
public void DoWork(IAsyncResult result)
{
Socket clientSocket = (Socket)result.AsyncState;
int bufferSize = clientSocket.EndReceive(result);
byte[] packet = new byte[bufferSize];
Array.Copy(_buffer, packet, packet.Length);
// Handle the packet
ushort packetLength = BitConverter.ToUInt16(packet, 0);
Console.WriteLine("Packet received... Length: {0}", packetLength);
_buffer = new byte[1024];
clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, this.DoWork, clientSocket);
}
public void Dispose()
{
}
}
An object that handles incoming connections:
public class Connector : IDisposable
{
private Socket _socket;
private Receiver _receiver;
private byte[] _buffer;
public Connector(Socket socket, Receiver receiver, byte[] buffer)
{
this._socket = socket;
this._receiver = receiver;
this._buffer = buffer;
}
public void DoWork()
{
_socket.BeginAccept(this.HandleAcceptedCallback,null);
}
public void Dispose()
{
}
private void HandleAcceptedCallback(IAsyncResult result)
{
Socket clientSocket = _socket.EndAccept(result);
_buffer = new byte[1024];
clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, _receiver.DoWork, clientSocket);
this.DoWork();
}
}
And then my console object:
static void Main(string[] args)
{
byte[] buffer = new byte[1024];
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
using (Receiver receiver = new Receiver(buffer))
using (Connector connector = new Connector(socket, receiver, buffer))
{
socket.Bind(11000);
socket.Listen(500);
connector.DoWork();
while (true)
Console.ReadLine();
}
}
I have been trying to play around with different approaches and have found out that if I move the DoWork method from the Receiver object into the Connector object and wire the callbacks up there, it all works fine.
I was wondering if anyone would be able to tell me where I am going wrong, or why this is not practical? I am new to using sockets and apologize for any misunderstandings.

C# Async Sockets - Get Data

I'm building a simple server, that contains some information and that will be using Async Sockets to get N Connections (I don't really know how many, they can range from 50 to 1000).
The main purpose of this server is to 'verify' the file integrity and version of clients connecting, and if needed send them the updated files so they can patch.
Right now, I'm on the part of getting my sockets running, I can display the data in the console, and everything is fine, problem is, my most important variables are on my 'DownloadServer' class, while my socket functions are on a class named 'ServerSocket'.
My question is, how can I access the data in DownloadServer(that contains an instance of ServerSocket) when said info is requested to my socket. I can't return the data received with Async Callbacks (because they must be void), so what would be the best way to go about this?
Here's my socket code, it only receives a client and prints data sent. What I want to do is acess the received info (likewise, a command for example) and return to the client the request info, for example, if the message received is "GetFileVersion", the server should reply back with the value of the variable it contains (it's a global variable named FILEVERSION), being the problem that I can't seem to find a way to acces that variable.
class ServerSocket
{
private Socket _socket;
private byte[] _buffer;
public ServerSocket()
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public void bind(int port)
{
_socket.Bind(new IPEndPoint(IPAddress.Any, port));
}
public void Listen(int backlog)
{
_socket.Listen(backlog);
}
public void Accept()
{
_socket.BeginAccept(AcceptCallBack, null);
}
private void AcceptCallBack(IAsyncResult ar)
{
Socket clientSocket = _socket.EndAccept(ar);
Accept();
IPEndPoint clientEndPoint = (IPEndPoint)clientSocket.RemoteEndPoint;
Console.WriteLine("Client Connected: {0}",clientEndPoint.Address.ToString());
_buffer = new byte[2048];
clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
}
private void ReceivedCallback(IAsyncResult ar)
{
try
{
Socket clientSocket = ar.AsyncState as Socket;
int size = clientSocket.EndReceive(ar);
byte[] packet = new byte[size];
Array.Copy(_buffer, packet, packet.Length);
String data = System.Text.Encoding.ASCII.GetString(packet, 0, packet.Length);
}
catch (Exception ex)
{
;
}
}
}
}
Thank you in advance!
EDIT:
What I'd like to do is something like String commandReceived = socket.Receive() (like you would do with synchronous sockets), or some way to acess that command received via that socket.
class DownloadServer
{
private static String UPDATEPATH="";
private static int FILEVERSION;
private static string ROOTPATH = "";
private String SQLUsername = "";
private String SQLPassword = "";
private String AccountDB = "";
private String ServerName = "";
private SqlConnection sqlConn;
//some logic methods
static void Main(string[] args){
ServerSecurity serverSecurity = new ServerSecurity();
ServerSocket serverSocket = new ServerSocket();
serv.Initialize();
serverSecurity.Initialize();
serverSocket.bind(15779);
serverSocket.Listen(1000);
serverSocket.Accept();
while (true)
{
Console.WriteLine("Input the command:");
var Input = Console.ReadLine();
if (Input == "Update")
{
serv.prepareUpdate();
}
else if (Input == "Exit")
{
Environment.Exit(0);
}
else if (Input == "TestEncryption")
{
Console.WriteLine("Enter the text to be encrypted:");
String plainText = Console.ReadLine();
Console.WriteLine("Original Text = {0}", plainText);
Console.WriteLine("Encrypted Text = {0}", serverSecurity.Encrypt(plainText));
Console.WriteLine("Decrypted Text = {0}", serverSecurity.Decrypt(serverSecurity.Encrypt(plainText)));
}
}
}
Add in your DownloadServer Class :
Socket NewSocket;
byte[] buffer = new buffer [1024];
NewSocket = serverSocket.Accept();
Then begin Receive from this NewSocket :
void Receive()
{
NewSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, r);
}
private void ReceiveCallback(IAsyncResult ar)
{
Socket CurrentClient = (Socket)ar.AsyncState;
int Received;
try { Received = CurrentClient.Receive(buffer); }
catch
{
return;
}
byte[] _buffer = new byte[Received];
Array.Copy(buffer, _buffer, Received);
string _Text = Encoding.Unicode.GetString(_buffer);
if(_Text == "GetVersion")
byte[] infoVersion = Encoding.Encoding.Default.GetBytes(FILEVERSION);
CurrentClient .Send(infoVersion )
}
This looks like a classic design problem where you are attempting to give a class too many responsibilities. In SOLID design, each class should have a single responsibility. The SocketServer would then only be responsible for creating the socket and sending/receiving data. Your DownloadServer would be responsible for creating data to be sent. Another class say ValidationServer, would be responsible for requesting data through the SocketServer and parsing it. A third class, a controller if you will, would be responsible for coordinating calls and passing data between these classes. In this way your SocketServer and DownloadServer would be decoupled and not require variables from each other.

Reading off a socket until end of line C#?

I'm trying to write a service that listens to a TCP Socket on a given port until an end of line is recived and then based on the "line" that was received executes a command.
I've followed a basic socket programming tutorial for c# and have come up with the following code to listen to a socket:
public void StartListening()
{
_log.Debug("Creating Maing TCP Listen Socket");
_mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, _port);
_log.Debug("Binding to local IP Address");
_mainSocket.Bind(ipLocal);
_log.DebugFormat("Listening to port {0}",_port);
_mainSocket.Listen(10);
_log.Debug("Creating Asynchronous callback for client connections");
_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public void OnClientConnect(IAsyncResult asyn)
{
try
{
_log.Debug("OnClientConnect Creating worker socket");
Socket workerSocket = _mainSocket.EndAccept(asyn);
_log.Debug("Adding worker socket to list");
_workerSockets.Add(workerSocket);
_log.Debug("Waiting For Data");
WaitForData(workerSocket);
_log.DebugFormat("Clients Connected [{0}]", _workerSockets.Count);
_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
catch (ObjectDisposedException)
{
_log.Error("OnClientConnection: Socket has been closed\n");
}
catch (SocketException se)
{
_log.Error("Socket Exception", se);
}
}
public class SocketPacket
{
private System.Net.Sockets.Socket _currentSocket;
public System.Net.Sockets.Socket CurrentSocket
{
get { return _currentSocket; }
set { _currentSocket = value; }
}
private byte[] _dataBuffer = new byte[1];
public byte[] DataBuffer
{
get { return _dataBuffer; }
set { _dataBuffer = value; }
}
}
private void WaitForData(Socket workerSocket)
{
_log.Debug("Entering WaitForData");
try
{
lock (this)
{
if (_workerCallback == null)
{
_log.Debug("Initializing worker callback to OnDataRecieved");
_workerCallback = new AsyncCallback(OnDataRecieved);
}
}
SocketPacket socketPacket = new SocketPacket();
socketPacket.CurrentSocket = workerSocket;
workerSocket.BeginReceive(socketPacket.DataBuffer, 0, socketPacket.DataBuffer.Length, SocketFlags.None, _workerCallback, socketPacket);
}
catch (SocketException se)
{
_log.Error("Socket Exception", se);
}
}
public void OnDataRecieved(IAsyncResult asyn)
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
try
{
int iRx = socketData.CurrentSocket.EndReceive(asyn);
char[] chars = new char[iRx + 1];
_log.DebugFormat("Created Char array to hold incomming data. [{0}]",iRx+1);
System.Text.Decoder decoder = System.Text.Encoding.UTF8.GetDecoder();
int charLength = decoder.GetChars(socketData.DataBuffer, 0, iRx, chars, 0);
_log.DebugFormat("Read [{0}] characters",charLength);
String data = new String(chars);
_log.DebugFormat("Read in String \"{0}\"",data);
WaitForData(socketData.CurrentSocket);
}
catch (ObjectDisposedException)
{
_log.Error("OnDataReceived: Socket has been closed. Removing Socket");
_workerSockets.Remove(socketData.CurrentSocket);
}
catch (SocketException se)
{
_log.Error("SocketException:",se);
_workerSockets.Remove(socketData.CurrentSocket);
}
}
This I thought was going to be a good basis for what I wanted to do, but the code I have appended the incoming characters to a text box one by one and didn't do anything with it. Which doesn't really work for what I want to do.
My main issue is the decoupling of the OnDataReceived method from the Wait for data method. which means I'm having issues building a string (I would use a string builder but I can accept multiple connections so that doesn't really work.
Ideally I'd like to look while listening to a socket until I see and end of line character and then call a method with the resulting string as a parameter.
What's the best way to go about doing this.
Try using asynch sockets. The code below will listening to a socket, and if the new line char through telnet is recieved it will echo it back out to the incomming socket. It seems like you would just need to redirect that input to your text box.
private string _hostName;
private const int _LISTENINGPORT = 23;
private Socket _incomingSocket;
byte[] _recievedData;
//todo: do we need 1024 byte? the asynch methods read the bytes as they come
//so when 1 byte typed == 1 byte read. Unless its new line then it is two.
private const int _DATASIZE = 1024;
public ConnectionServer()
{
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
_hostName = Dns.GetHostName();
_recievedData = new byte[_DATASIZE];
_incomingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(localAddr, _LISTENINGPORT);
_incomingSocket.Bind(endPoint);
_incomingSocket.Listen(10);
}
~ConnectionServer()
{
}
public void StartListening()
{
_incomingSocket.BeginAccept(new AsyncCallback(OnAccept), _incomingSocket);
}
private void OnAccept(IAsyncResult result)
{
UserConnection connectionInfo = new UserConnection();
Socket acceptedSocket = (Socket)result.AsyncState;
connectionInfo.userSocket = acceptedSocket.EndAccept(result);
connectionInfo.messageBuffer = new byte[_DATASIZE];
//Begin acynch communication with target socket
connectionInfo.userSocket.BeginReceive(connectionInfo.messageBuffer, 0, _DATASIZE, SocketFlags.None,
new AsyncCallback(OnReceiveMessage), connectionInfo);
//reset the listnening socket to start accepting
_incomingSocket.BeginAccept(new AsyncCallback(OnAccept), result.AsyncState);
}
private void OnReceiveMessage(IAsyncResult result)
{
UserConnection connectionInfo = (UserConnection)result.AsyncState;
int bytesRead = connectionInfo.userSocket.EndReceive(result);
if (connectionInfo.messageBuffer[0] != 13 && connectionInfo.messageBuffer[1] != 10)
//ascii for newline and line feed
//todo dress this up
{
if (string.IsNullOrEmpty(connectionInfo.message))
{
connectionInfo.message = ASCIIEncoding.ASCII.GetString(connectionInfo.messageBuffer);
}
else
{
connectionInfo.message += ASCIIEncoding.ASCII.GetString(connectionInfo.messageBuffer);
}
}
else
{
connectionInfo.userSocket.Send(ASCIIEncoding.ASCII.GetBytes(connectionInfo.message), SocketFlags.None);
connectionInfo.userSocket.Send(connectionInfo.messageBuffer, SocketFlags.None);
connectionInfo.message = string.Empty;
connectionInfo.messageBuffer = new byte[_DATASIZE];
}
{
public class UserConnection
{
public Socket userSocket { get; set; }
public Byte[] messageBuffer { get; set; }
public string message { get; set; }
}
}
You seem to have several issues:
You have an asynchronous method called WaitForData. That's very confusing, as methods with the word Wait in their names generally block the currently executing thread until something happens (or, optionally, a timeout expires). This does the exact opposite. Are you intending for this to be a synchronous or asynchronous operation?
There's also no need to instantiate the Decoder object, nor do you need the char array for (it seems) anything; just call System.Text.Encoding.UTF8.GetString(socketData.DataBuffer, 0, iRx).
You also don't appear to be doing anything with lines...which is why it doesn't do anything with lines.
Your approach with using a StringBuilder is what I would do. I would add a StringBuilder to the SocketData class and call it Builder. As you capture string data, do something like this:
string[] data = System.Text.Encoding.UTF8.GetString(
socketData.DataBuffer, 0, iRx).Split(Environment.NewLine);
socketData.Builder.Append(data[0]);
for(int i = 1; i < data.Length; i++)
{
// the socketData.Builder variable now contains a single line, so do
// something with it here, like raise an event
OnLineReceived(builder.ToString());
socketData.Builder = new StringBuilder(data[i]);
}
The one caveat here is that UTF8 is a multi-byte encoding, meaning that you could potentially grab a chunk of data that cuts off mid-character. It's generally a better idea to do this sort of preprocessing on the other side of the communication, then send the data in an appropriately length-prefixed format.

Categories

Resources