How can you maximize the throughput on a single TCP socket? - c#

I am trying to get the standard example "echo" client/server application to run as quickly as possible and I am sure that the network is a limiting factor. I have a 1 gigabit network card and when I use resource monitor I am only getting 7 megabit out of the client.
I understand the basics of sockets, message framing and length indicators, receiving all bytes indicated by the length indicator. Keep alive packets, half open connections etc.
I started out using stock standard socket operations and then switched to using async. (I didn't change the send to async because someone [who seemed knowledgeable said it shouldn't have any impact]) I have the exact same performance and I can only think that all the material was assuming that I some other work that could be done on the same thread. But in my quick test I had dedicated 1 thread to conterminously spin in a loop sending and another completely different thread to receive.
I have tried everything and am completely lost as to where I can get more performance. I used IPerf and that reported back speeds of 1 gigabit per second and resource monitor also showed that to be chewing up the bandwidth.
Even if someone can maybe point me in the direction of a more complete example. Most of the ones I have come across are trivial or incomplete.
Here is the general code.
class Program
{
private static Socket sock;
private static BlockingCollection<string> queue;
private static int bytesReceived;
private static byte[] dataBuffer;
private static readonly byte[] lengthBuffer = new byte[4];
private static byte[] PrependLengthIndicator(byte[] data)
{
return BitConverter.GetBytes(data.Length).Concat(data).ToArray();
}
private static void Receive()
{
if (dataBuffer == null)
{
sock.BeginReceive(lengthBuffer, 0, 4, SocketFlags.None, ReceiveCallback, null);
}
else
{
sock.BeginReceive(dataBuffer, 0, bytesReceived, SocketFlags.None, ReceiveCallback, null);
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
bytesReceived += sock.EndReceive(ar);
if (dataBuffer == null)
{
// Currently receiving length indicator
if (bytesReceived >= 4)
{
var length = BitConverter.ToInt32(lengthBuffer, 0);
dataBuffer = new byte[length];
bytesReceived = 0;
}
}
else
{
if (bytesReceived == dataBuffer.Length)
{
// Finished reading
var request = Encoding.ASCII.GetString(dataBuffer);
dataBuffer = null;
bytesReceived = 0;
queue.Add(request);
}
}
ContinueReading();
}
private static void ContinueReading()
{
// Read into the appropriate buffer: length or data
if (dataBuffer != null)
{
sock.BeginReceive(dataBuffer, bytesReceived, dataBuffer.Length - bytesReceived, SocketFlags.None, ReceiveCallback, null);
}
else
{
sock.BeginReceive(lengthBuffer, bytesReceived, lengthBuffer.Length - bytesReceived, SocketFlags.None, ReceiveCallback, null);
}
}
}
Here is the server portion:
static void Main(string[] args)
{
var listenSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSock.Bind(new IPEndPoint(IPAddress.Parse(ConfigurationManager.AppSettings["LocalIp"]), 3333));
listenSock.Listen(10);
Console.WriteLine("Server started...");
sock = listenSock.Accept();
Console.WriteLine("Connection accepted.");
queue = new BlockingCollection<string>();
Receive();
var count = 0;
var sender = new Thread(() =>
{
while (true)
{
var bar = queue.Take() + "Resp";
count++;
var resp = Encoding.ASCII.GetBytes(bar);
var toSend = PrependLengthIndicator(resp);
if (count % 10000 == 0)
{
Console.WriteLine(bar);
}
sock.Send(toSend);
}
});
sender.Start();
}
Here is the client portion:
static void Main(string[] args)
{
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine("Connecting...");
sock.Connect(IPAddress.Parse(ConfigurationManager.AppSettings["EndPointIp"]), 3333);
Console.WriteLine("Connected.");
Receive();
var count = 0;
while(true)
{
count++;
var foo = "Echo-" + count;
var data = Encoding.ASCII.GetBytes(foo);
var toSend = PrependLengthIndicator(data);
sock.Send(toSend);
}
}

You are sending tiny messages. Think about how many millions of them you would need to saturate a 1 Gbit/sec link. Each call to a socket burns CPU.
Send bigger messages. Also, try not to allocate new buffers all the time. Don't use Enumerable.Concat to concatenate byte buffers because that operates byte-by-byte in a horribly inefficient way. Use Array.Copy with preallocated arrays.
If you are using very few threads switch to synchronous IO as it will be faster (really! it has less overhead).
You can confirm that this answer is correct by running a dumb infinite send loop that just sends buffers of 64KB synchronously all the time. It will saturate the link.

Related

How can I make an App visible on the LAN so that I can connect to another C# App? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I created a Windows Form application that can connect to another one on the same PC but it won't work on the LAN , I used sockets but I didn't fully understood it and I am not that great at C#.
I have the following code for the server:
private byte[] _buffer = new byte[4096];
public List<SocketT2h> __ClientSockets { get; set; }
List<string> _names = new List<string>();
private Socket _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public Main()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
__ClientSockets = new List<SocketT2h>();
}
private void Main_Load(object sender, EventArgs e)
{
contextMenuStrip1.ItemClicked += new ToolStripItemClickedEventHandler(contextMenuStrip1_ItemClicked);
SetupServer();
}
private void SetupServer()
{
Console_textBox.Text += "Setting up server . . .\n";
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, 100));
_serverSocket.Listen(1);
_serverSocket.BeginAccept(new AsyncCallback(AppceptCallback), null);
Console_textBox.Text += "Server is online !";
}
private void AppceptCallback(IAsyncResult ar)
{
Socket socket = _serverSocket.EndAccept(ar);
__ClientSockets.Add(new SocketT2h(socket));
Console_textBox.Text = "Client connected. . .";
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
_serverSocket.BeginAccept(new AsyncCallback(AppceptCallback), null);
}
And this is the code for the client:
public Form1()
{
InitializeComponent();
LoopConnect();
_clientSocket.BeginReceive(receivedBuf, 0, receivedBuf.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _clientSocket);
byte[] buffer = Encoding.ASCII.GetBytes("Alex : ");
_clientSocket.Send(buffer);
}
private Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
byte[] receivedBuf = new byte[4096];
private void ReceiveData(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
int received = socket.EndReceive(ar);
byte[] dataBuf = new byte[received];
Array.Copy(receivedBuf, dataBuf, received);
_clientSocket.BeginReceive(receivedBuf, 0, receivedBuf.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _clientSocket);
}
catch { }
}
private void SendLoop()
{
while (true)
{
try
{
byte[] receivedBuf = new byte[4096];
int rev = _clientSocket.Receive(receivedBuf);
if (rev != 0)
{
byte[] data = new byte[rev];
Array.Copy(receivedBuf, data, rev);
}
else _clientSocket.Close();
}
catch { }
}
}
private void LoopConnect()
{
try
{
int attempts = 0;
while (!_clientSocket.Connected)
{
try
{
attempts++;
_clientSocket.Connect(IPAddress.Loopback, 100);
}
catch (SocketException)
{
}
}
}
catch { }
}
I also added the ports used by the Apps to the FireWall.
If I'm doing it completely wrong, I'm sorry, but I'm still learning.
You're probably going about this in a difficult to get working/reliable way. To read/write continually you need loops or callbacks, to have loops and a working UI you need threads, you're using IOCP callbacks which end up running on different threads. Different threads can't update the UI so you need CheckForIllegalCrossThreadCalls = false; which is bad, so you next need some other syncronisation and a way to keep it all together and have a responsive UI.
Given you said that you didn't fully understand it and are new to C# I will give you an alternative idea using the ASYNC/AWAIT pattern and Tasks. This allows you to seemingly have loops, but while the loops are waiting for network input/output they aren;t actually looping so your UI is responsive.
Firstly we create a server from your GUI:
private async Task StartServerAsync()
{
var tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 9999));
tcpListener.Start();
btnStartServer.Enabled = false;
lbConnectedClients.Items.Add("TcpListener has started listening for clients.");
while (true)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
_ = StartServerSideClient(tcpClient);
}
}
private async void btnStartServer_Click(object sender, EventArgs e)
{
await StartServerAsync();
}
So in your GUI clicking the Button btnStartServer adds an entry to the ListBox lbConnectedClients telling us that the server has started. We also stop the user from being able to start the server more than once by disabing the bthStartServer buttong
After the user has clicked btnStartServer the socket is now sitting there waiting for connections inside the while (true) loop. Because we're using a full async/await pattern, the loop isn't actually looping, it's sitting in the background waiting for a client to connect doing absolutely nothing. So your UI can continue doing what it does best.
Next we have another button and another listbox on UI. The second button starts a client.
private async Task StartEchoClientAsync()
{
using (var tcpClient = new TcpClient("127.0.0.1", 9999))
{
lbEchoClients.Items.Add("TcpClient: " + tcpClient.Client.LocalEndPoint + "created.");
var s = tcpClient.GetStream();
var buffer = new byte[1000];
for(var i = 0; i < 10; i++)
{
await Task.Delay(1000);
await s.WriteAsync(buffer, 0, buffer.Length);
lbEchoClients.Items.Add("Sent Bytes: " + buffer.Length);
await s.ReadAsync(buffer, 0, buffer.Length);
lbEchoClients.Items.Add("Read Bytes: " + buffer.Length);
}
tcpClient.Close();
}
}
private async void btnStartAClient_Click(object sender, EventArgs e)
{
await StartEchoClientAsync();
}
Now in this example all the client does is sends data to the server and reads the data back. Again there is a loop, again that loop is spending most of its time waiting 1 second with the await Task.Delay(1000) this isn't like a Thread.Sleep(1000) it lets your UI just get on with doing what it needs to do while the Send/Read loop is doing absolutely nothing.
Finally we need a "client" on the server side. This client is responsible for handling each connection to the Server. Remember once a socket starts listening it can receive as many connections as memory permits.
private async Task StartServerSideClient(TcpClient tcpClient)
{
lbConnectedClients.Items.Add("Client: " + tcpClient.Client.RemoteEndPoint.ToString() + " Connected");
var s = tcpClient.GetStream();
var buffer = new byte[1000];
int bytesRead = 0;
int bytesWrote = 0;
while (true)
{
int rt = await s.ReadAsync(buffer, 0, buffer.Length);
if (rt == 0) // If ReadAsync() returns with zero then the underlying stream/socket is closed
break;
bytesRead += buffer.Length;
var wt = s.WriteAsync(buffer, 0, buffer.Length);
bytesWrote += buffer.Length;
}
lbConnectedClients.Items.Add("Client: " + tcpClient.Client.RemoteEndPoint.ToString() + " Wrote: " + bytesWrote + " Read: " + bytesRead);
lbConnectedClients.Items.Add("Client: " + tcpClient.Client.RemoteEndPoint.ToString() + " disconnected");
s.Close();
}
So now everytime a client connects we simply read anything it gives us, and just send it straight back. Again there is another loop, but again because we are using async/await to do the reading/writing it doesn't block your UI.
Everything above is running on the UI thread but because it's using the async/await pattern it never blocks the UI.
In the real world it's unlikely that your server and client would be on the same GUI.

Async socket client using TcpClient class C#

I have implemented a socket client using TcpClient class. So I can send and receive data, everything works good. But I ask some of the guru's out there :) Is there something wrong with my implementation? maybe there is a better way of doing things. In particular, how do I handle disconnects? Is there some indicator (or maybe I can write one myself) that tells me that socket has disconnected?
I also have looked into async await features of Socket class, but can't wrap my head around "SocketAsyncEventArgs", why is it there in the first place.
Why cant i just: await Client.SendAsync("data"); ?
public class Client
{
private TcpClient tcpClient;
public void Initialize(string ip, int port)
{
try
{
tcpClient = new TcpClient(ip, port);
if (tcpClient.Connected)
Console.WriteLine("Connected to: {0}:{1}", ip, port);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Initialize(ip, port);
}
}
public void BeginRead()
{
var buffer = new byte[4096];
var ns = tcpClient.GetStream();
ns.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
}
public void EndRead(IAsyncResult result)
{
var buffer = (byte[])result.AsyncState;
var ns = tcpClient.GetStream();
var bytesAvailable = ns.EndRead(result);
Console.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytesAvailable));
BeginRead();
}
public void BeginSend(string xml)
{
var bytes = Encoding.ASCII.GetBytes(xml);
var ns = tcpClient.GetStream();
ns.BeginWrite(bytes, 0, bytes.Length, EndSend, bytes);
}
public void EndSend(IAsyncResult result)
{
var bytes = (byte[])result.AsyncState;
Console.WriteLine("Sent {0} bytes to server.", bytes.Length);
Console.WriteLine("Sent: {0}", Encoding.ASCII.GetString(bytes));
}
}
And the usage:
static void Main(string[] args)
{
var client = new Client();
client.Initialize("127.0.0.1", 8778);
client.BeginRead();
client.BeginSend("<Names><Name>John</Name></Names>");
Console.ReadLine();
}
Okay it took me 10 seconds to find biggest issue you could've done :
public void BeginRead()
{
var buffer = new byte[4096];
var ns = tcpClient.GetStream();
ns.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
}
But don't worry that's why we are on SO.
First let me explain why it's such a big issue.
Assume that you're sending message which is 4097 bytes long. Your buffer can only accept 4096 bytes meaning that you cannot pack whole message into this buffer.
Assume you're sending message which is 12 bytes long. You're still allocating 4096 bytes in your memory just to store 12 bytes.
How to deal with this?
Every time you work with networking you should consider making some kind of protocol ( some people call it message framing, but it's just a protocol ) that will help you to identify incomming package.
Example of a protocol could be :
[1B = type of message][4B = length][XB = message]
- where X == BitConvert.ToInt32(length);
Receiver:
byte messageType = (byte)netStream.ReadByte();
byte[] lengthBuffer = new byte[sizeof(int)];
int recv = netStream.Read(lengthBuffer, 0, lengthBuffer.Length);
if(recv == sizeof(int))
{
int messageLen = BitConverter.ToInt32(lengthBuffer, 0);
byte[] messageBuffer = new byte[messageLen];
recv = netStream.Read(messageBuffer, 0, messageBuffer.Length);
if(recv == messageLen)
{
// messageBuffer contains your whole message ...
}
}
Sender:
byte messageType = (1 << 3); // assume that 0000 1000 would be XML
byte[] message = Encoding.ASCII.GetBytes(xml);
byte[] length = BitConverter.GetBytes(message.Length);
byte[] buffer = new byte[sizeof(int) + message.Length + 1];
buffer[0] = messageType;
for(int i = 0; i < sizeof(int); i++)
{
buffer[i + 1] = length[i];
}
for(int i = 0; i < message.Length; i++)
{
buffer[i + 1 + sizeof(int)] = message[i];
}
netStream.Write(buffer);
Rest of your code looks okay. But in my opinion using asynchronous operations in your case is just useless. You can do the same with synchronous calls.
It's hard to answer because theres no exact question here but more some kind of code review. But still some hints:
Your connect mechanism seems wrong. I don't think TcpClient.Connected will block until the connection was established. So it will often just fail as the connection is in progress and then you start all over again. You should switch to using a blocking or async Connect method.
SocketAsyncEventArgs is a mechanism for high performance async data transmission. There's rarely a need for it. You should just ignore it
If you want to send data asynchronously you should use the Async methods which return a Task, because these can be easily combined with async/await.
The APM model (BeginXYZ/EndXYZ) is kind of deprecated, you shouldn't use it anymore in new code. One issue with it is that sometimes the End method is called synchronously inside the Begin method which can lead to surprising behavior. If this is not the case the completion callback will be performed from a random thread on the ThreadPool. This is also often not what you want. The TPL methods avoid this.
For your simple use case the blocking methods are also totally fine and do not come with the complexity of the various async methods.
The reading side of the code with TPL methods (untested):
public async Task Initialize(string ip, int port)
{
tcpClient = new TcpClient;
await tcpClient.ConnectAsync(ip, port);
Console.WriteLine("Connected to: {0}:{1}", ip, port);
}
public async Task Read()
{
var buffer = new byte[4096];
var ns = tcpClient.GetStream();
while (true)
{
var bytesRead = await ns.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) return; // Stream was closed
Console.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
}
In the initialization part you would do:
await client.Initialize(ip, port);
// Start reading task
Task.Run(() => client.Read());
For using synchronous methods delete all Async occurences and replace Task with Thread.

My received data slipt in two part in socket c#

I have this code as a server application to receive data :
public AsyncCallback pfnWorkerCallBack;
private Socket m_mainSocket;
private Socket[] m_workerSocket = new Socket[25];
private int m_clientCount = 0;
public void startfun()
{
string Camera1Port = "1001";
int port1 = System.Convert.ToInt32(Camera1Port);
m_mainSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port1);
m_mainSocket.Bind(ipLocal);
m_mainSocket.Listen(20);
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public void OnClientConnect(IAsyncResult asyn)
{
m_workerSocket[m_clientCount] = m_mainSocket.EndAccept(asyn);
WaitForData(m_workerSocket[m_clientCount]);
++m_clientCount;
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public class SocketPacket
{
public System.Net.Sockets.Socket m_currentSocket;
public byte[] dataBuffer ;
}
public void WaitForData(System.Net.Sockets.Socket soc)
{
if (pfnWorkerCallBack == null)
{
pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
}
SocketPacket theSocPkt = new SocketPacket();
theSocPkt.dataBuffer = new byte[soc.ReceiveBufferSize];
theSocPkt.m_currentSocket = soc;
soc.BeginReceive(theSocPkt.dataBuffer, 0,
theSocPkt.dataBuffer.Length,
SocketFlags.None,
pfnWorkerCallBack,
theSocPkt);
}
public void OnDataReceived(IAsyncResult asyn)
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
string res = GetParameters(socketData.dataBuffer);
MessageBox.Show(res);
WaitForData(socketData.m_currentSocket);
}
public string GetParameters(byte[] buf)
{
string result = System.Text.Encoding.UTF8.GetString(buf);
return result;
}
The problem is the data is breaking .I mean when i receive data from socket in first time all values are received and the messagebox show all of that.when i receive the second data ,i just receive some part of that .suppose the data is 870314854798 .I receive the data in first time correctly ,but in second i just get 3 or 4 digit of that(it is random) and the message box shows 3 or 4 digit after clicking of ok (Messagebox) the other digits are shown.why ?
i googled it and i found this but i can't map this solution with my solution
public static void Read_Callback(IAsyncResult ar){
StateObject so = (StateObject) ar.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(ar);
if (read > 0) {
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
new AsyncCallback(Async_Send_Receive.Read_Callback), so);
}
else{
if (so.sb.Length > 1) {
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
s.Close();
}
}
The problem is how can i merge this solution with my code ?
In your OnDataReceived method you should EndReceive to complete the operation you began like this:
StateObject so = (StateObject) async.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(async); // You need to do this.
You should always call, as per MSDN's recommendations, the EndReceive, EndInvoke etc on any async operation. Otherwise, you may not get the exceptions either.
That's how network stack (especially TCP) works. It does not guarantee that all data sent into a socket as a single piece would be delivered to the client as a single piece. You can only expect that all data sent into a socket will be received by the client in the same order, but can't rely on how it is going to be split into chunks.
E.g. as the data travel through the network they might pass nodes that have different settings and limitations on how big the packet may be. If a packet is too large, it will be split into multiple packets of appropriate size that will be then sent to the client.
If your protocol requires that a packet is delivered from the server to the client as a single solid piece, you would have to construct your own application layer protocol to control that. This is how http works: no matter how the data representing a web page is split into TCP packets, the client receives the whole page at once.

Bridge a Asynchronous full Duplex TCP communication between 2 listeners

I am in need of a C# layer which will help to exchange the data between two TCP ports which are listening.
For example, There is a listener port # 192.168.1.2::5555 and another listener port # 192.168.1.4::6666.
I am able to establish the connection to both the listeners using socket.connect
I am getting confused during creation of 2 threads
1> Sock1.read()->convert to bytes ->sock2 .write()
2> Sock2.read()->Convert to bytes -> Sock1.write()
I think this is entering into the infinite loop. Is there any better way of exchanging packets between 2 Listening ports by establishing connection to both ports?
I have to implement a method
Private void ExchangePackets(IpEndpoint ipe1,IpEndpoint ipe2)
{
//code here
}
It can be something like this:(Not tested)
void ExchangePackets(IPEndPoint ipe1, IPEndPoint ipe2)
{
TcpClient tcp1 = new TcpClient();
tcp1.Connect(ipe1);
TcpClient tcp2 = new TcpClient();
tcp2.Connect(ipe2);
Task.Factory.StartNew(() => ByPass(tcp1, tcp2), TaskCreationOptions.LongRunning);
Task.Factory.StartNew(() => ByPass(tcp2, tcp1), TaskCreationOptions.LongRunning);
}
void ByPass(TcpClient tcp1, TcpClient tcp2)
{
using (tcp1)
using (tcp2)
{
Stream s1 = tcp1.GetStream();
Stream s2 = tcp2.GetStream();
byte[] buf = new byte[0x10000];
int len = s1.Read(buf, 0, buf.Length);
while (len > 0)
{
s2.Write(buf, 0, len);
len = s1.Read(buf, 0, buf.Length);
}
}
}
$Finally Achieved it :-) $
private void Form1_Load(object sender, EventArgs e)
{
RoutingClient=new TcpClient("127.0.0.1",5765);
ServerClient = new TcpClient("127.0.0.1", 5766);
rcStream = RoutingClient.GetStream();
ScStream = ServerClient.GetStream();
//start 2 threads
Thread t1 = new Thread(()=>ExchangePackets(rcStream,ScStream));
t1.Start();
Thread t2 = new Thread(() => ExchangePackets(ScStream, rcStream));
t2.Start();
}
private static void ExchangePackets(NetworkStream FirstStream, NetworkStream SecondStream)
{
try
{
while (true)
{
if (FirstStream.CanRead)
{
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
MemoryStream ms = new MemoryStream();
int numberOfBytesRead = 0;
int TotalBytesRead = 0;
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = FirstStream.Read(myReadBuffer, 0, myReadBuffer.Length);
ms.Write(myReadBuffer, TotalBytesRead, numberOfBytesRead);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
TotalBytesRead = TotalBytesRead + numberOfBytesRead;
}
while (FirstStream.DataAvailable);
MessageBox.Show("You received the following message : " +
myCompleteMessage);
if (SecondStream.CanWrite)
{
byte[] myWriteBuffer = ms.ToArray();
SecondStream.Write(myWriteBuffer, 0, myWriteBuffer.Length);
}
else
{
MessageBox.Show("Sorry. You cannot write to this NetworkStream.");
}
}
else
{
MessageBox.Show("Sorry. You cannot read from this NetworkStream.");
}
}
}
catch (Exception ex)
{
MessageBox.Show("Routing to Server:" + ex.Message);
}
}
EDIT: On closer examination, I don't think my answer is suitable for your situation. Your question describes a number of fixed constraints that might prevent you implementing it this way. I will leave my answer here for other people though, as I do think it is generally the right approach.
Is there any better way of exchanging packets between 2 Listening
ports by establishing connection to both ports?
You don't need to reinvent the wheel :)
You can use WCF to create a full-duplex service with TCP transport?

Sending and receiving data over a network using TcpClient

I need to develop a service that will connect to a TCP server. Main tasks are reading incoming messages and also sending commands to the server in ten minutes, like a synchronize command. For example, I used the TcpClient object as shown below:
...
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("x.x.x.x", 9999);
networkStream = tcpClient.GetStream();
clientStreamReader = new StreamReader(networkStream);
clientStreamWriter = new StreamWriter(networkStream);
while(true)
{
clientStreamReader.Read()
}
Also, when I need to write out something in any method, I use:
clientStreamWriter.write("xxx");
Is this usage correct? Or is there a better way?
First, I recommend that you use WCF, .NET Remoting, or some other higher-level communication abstraction. The learning curve for "simple" sockets is nearly as high as WCF, because there are so many non-obvious pitfalls when using TCP/IP directly.
If you decide to continue down the TCP/IP path, then review my .NET TCP/IP FAQ, particularly the sections on message framing and application protocol specifications.
Also, use asynchronous socket APIs. The synchronous APIs do not scale and in some error situations may cause deadlocks. The synchronous APIs make for pretty little example code, but real-world production-quality code uses the asynchronous APIs.
Be warned - this is a very old and cumbersome "solution".
By the way, you can use serialization technology to send strings, numbers or any objects which are support serialization (most of .NET data-storing classes & structs are [Serializable]).
There, you should at first send Int32-length in four bytes to the stream and then send binary-serialized (System.Runtime.Serialization.Formatters.Binary.BinaryFormatter) data into it.
On the other side or the connection (on both sides actually) you definetly should have a byte[] buffer which u will append and trim-left at runtime when data is coming.
Something like that I am using:
namespace System.Net.Sockets
{
public class TcpConnection : IDisposable
{
public event EvHandler<TcpConnection, DataArrivedEventArgs> DataArrive = delegate { };
public event EvHandler<TcpConnection> Drop = delegate { };
private const int IntSize = 4;
private const int BufferSize = 8 * 1024;
private static readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
private readonly TcpClient _tcpClient;
private readonly object _droppedRoot = new object();
private bool _dropped;
private byte[] _incomingData = new byte[0];
private Nullable<int> _objectDataLength;
public TcpClient TcpClient { get { return _tcpClient; } }
public bool Dropped { get { return _dropped; } }
private void DropConnection()
{
lock (_droppedRoot)
{
if (Dropped)
return;
_dropped = true;
}
_tcpClient.Close();
_syncContext.Post(delegate { Drop(this); }, null);
}
public void SendData(PCmds pCmd) { SendDataInternal(new object[] { pCmd }); }
public void SendData(PCmds pCmd, object[] datas)
{
datas.ThrowIfNull();
SendDataInternal(new object[] { pCmd }.Append(datas));
}
private void SendDataInternal(object data)
{
if (Dropped)
return;
byte[] bytedata;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
try { bf.Serialize(ms, data); }
catch { return; }
bytedata = ms.ToArray();
}
try
{
lock (_tcpClient)
{
TcpClient.Client.BeginSend(BitConverter.GetBytes(bytedata.Length), 0, IntSize, SocketFlags.None, EndSend, null);
TcpClient.Client.BeginSend(bytedata, 0, bytedata.Length, SocketFlags.None, EndSend, null);
}
}
catch { DropConnection(); }
}
private void EndSend(IAsyncResult ar)
{
try { TcpClient.Client.EndSend(ar); }
catch { }
}
public TcpConnection(TcpClient tcpClient)
{
_tcpClient = tcpClient;
StartReceive();
}
private void StartReceive()
{
byte[] buffer = new byte[BufferSize];
try
{
_tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, DataReceived, buffer);
}
catch { DropConnection(); }
}
private void DataReceived(IAsyncResult ar)
{
if (Dropped)
return;
int dataRead;
try { dataRead = TcpClient.Client.EndReceive(ar); }
catch
{
DropConnection();
return;
}
if (dataRead == 0)
{
DropConnection();
return;
}
byte[] byteData = ar.AsyncState as byte[];
_incomingData = _incomingData.Append(byteData.Take(dataRead).ToArray());
bool exitWhile = false;
while (exitWhile)
{
exitWhile = true;
if (_objectDataLength.HasValue)
{
if (_incomingData.Length >= _objectDataLength.Value)
{
object data;
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(_incomingData, 0, _objectDataLength.Value))
try { data = bf.Deserialize(ms); }
catch
{
SendData(PCmds.Disconnect);
DropConnection();
return;
}
_syncContext.Post(delegate(object T)
{
try { DataArrive(this, new DataArrivedEventArgs(T)); }
catch { DropConnection(); }
}, data);
_incomingData = _incomingData.TrimLeft(_objectDataLength.Value);
_objectDataLength = null;
exitWhile = false;
}
}
else
if (_incomingData.Length >= IntSize)
{
_objectDataLength = BitConverter.ToInt32(_incomingData.TakeLeft(IntSize), 0);
_incomingData = _incomingData.TrimLeft(IntSize);
exitWhile = false;
}
}
StartReceive();
}
public void Dispose() { DropConnection(); }
}
}
That is just an example, you should edit it for your use.
I have had luck using the socket object directly (rather than the TCP client). I create a Server object that looks something like this (I've edited some stuff such as exception handling out for brevity, but I hope that the idea comes across.)...
public class Server()
{
private Socket sock;
// You'll probably want to initialize the port and address in the
// constructor, or via accessors, but to start your server listening
// on port 8080 and on any IP address available on the machine...
private int port = 8080;
private IPAddress addr = IPAddress.Any;
// This is the method that starts the server listening.
public void Start()
{
// Create the new socket on which we'll be listening.
this.sock = new Socket(
addr.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
// Bind the socket to the address and port.
sock.Bind(new IPEndPoint(this.addr, this.port));
// Start listening.
this.sock.Listen(this.backlog);
// Set up the callback to be notified when somebody requests
// a new connection.
this.sock.BeginAccept(this.OnConnectRequest, sock);
}
// This is the method that is called when the socket recives a request
// for a new connection.
private void OnConnectRequest(IAsyncResult result)
{
// Get the socket (which should be this listener's socket) from
// the argument.
Socket sock = (Socket)result.AsyncState;
// Create a new client connection, using the primary socket to
// spawn a new socket.
Connection newConn = new Connection(sock.EndAccept(result));
// Tell the listener socket to start listening again.
sock.BeginAccept(this.OnConnectRequest, sock);
}
}
Then, I use a separate Connection class to manage the individual connection with the remote host. That looks something like this...
public class Connection()
{
private Socket sock;
// Pick whatever encoding works best for you. Just make sure the remote
// host is using the same encoding.
private Encoding encoding = Encoding.UTF8;
public Connection(Socket s)
{
this.sock = s;
// Start listening for incoming data. (If you want a multi-
// threaded service, you can start this method up in a separate
// thread.)
this.BeginReceive();
}
// Call this method to set this connection's socket up to receive data.
private void BeginReceive()
{
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
// This is the method that is called whenever the socket receives
// incoming bytes.
protected void OnBytesReceived(IAsyncResult result)
{
// End the data receiving that the socket has done and get
// the number of bytes read.
int nBytesRec = this.sock.EndReceive(result);
// If no bytes were received, the connection is closed (at
// least as far as we're concerned).
if (nBytesRec <= 0)
{
this.sock.Close();
return;
}
// Convert the data we have to a string.
string strReceived = this.encoding.GetString(
this.dataRcvBuf, 0, nBytesRec);
// ...Now, do whatever works best with the string data.
// You could, for example, look at each character in the string
// one-at-a-time and check for characters like the "end of text"
// character ('\u0003') from a client indicating that they've finished
// sending the current message. It's totally up to you how you want
// the protocol to work.
// Whenever you decide the connection should be closed, call
// sock.Close() and don't call sock.BeginReceive() again. But as long
// as you want to keep processing incoming data...
// Set up again to get the next chunk of data.
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
}
You can use your Connection object to send data by calling its Socket directly, like so...
this.sock.Send(this.encoding.GetBytes("Hello to you, remote host."));
As I said, I've tried to edit the code here for posting, so I apologize if there are any errors in it.
First of all, TCP does not guarantee that everything that you send will be received with the same read at the other end. It only guarantees that all bytes that you send will arrive and in the correct order.
Therefore, you will need to keep building up a buffer when reading from the stream. You will also have to know how large each message is.
The simplest ever is to use a non-typeable ASCII character to mark the end of the packet and look for it in the received data.
I've developed a dotnet library that might come in useful. I have fixed the problem of never getting all of the data if it exceeds the buffer, which many posts have discounted. Still some problems with the solution but works descently well https://github.com/Apollo013/DotNet-TCP-Communication

Categories

Resources