I am writing an application that will require hundreds of socket connections via tcp to read / write data.
You do not need "high performance sockets" for this. The code is much simpler with normal performance sockets.
First, do not use custom expectations from the link you provided. They are great for some people (and completely "reliable"), but you donβt need them, and your code will be easier without them.
- Is there something wrong with this, and is it possible to optimize it?
Yes. You should not mix lock ( Connect ) and asynchronous ( ReadAsync ) code. I would recommend something like this:
foreach (var ip in listofIps) { IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ip), 4001); Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await client.ConnectTaskAsync(remoteEP); ... }
Where ConnectTaskAsync is the standard TAP on top of the APM shell :
public static Task ConnectTaskAsync(this Socket socket, EndPoint endpoint) { return TaskFactory.FromAsync(socket.BeginConnect, socket.EndConnect, endpoint, null); }
As Mark Gravell noted, this code (and your source code) connects the sockets one at a time. You can use Task.WhenAll to connect all of them at once.
2) Is there a more efficient way to do this?
First, you must define a TAP-over-APM ReceiveTaskAsync similar to the one described above. When working with binary data, I also like to have an extension method in byte arrays to reset:
public string DumpHex(this ArraySegment<byte> data) { return string.Join(" ", data.Select(b => b.ToString("X2"))); }
Then you might have this code:
while (true) { int bytesRead = await socket.ReceiveTaskAsync(buffer); if (bytesRead == 0) break; var data = new ArraySegment<byte>(buffer, 0, bytesRead); AppendLog("RX: " + data.HexDump()); ... }
If you do a lot of binary manipulations, you can find my ArraySegments library .
3) Where and how to enable logic to check if all my data got into one read
Oh, this is harder than that. :) Sockets are an abstraction of a stream, not an abstraction of a message. Therefore, if you want to define βmessagesβ in your protocol, you need to specify a length prefix or byte separator so that you can detect message boundaries. Then you need to write code that will analyze your messages, bearing in mind that the data blocks read from the socket can contain only a partial message (so you need to buffer it), a full message, several full messages, and can also complete a partial message (again, buffering). And you should also consider your existing buffer when receiving a new block.
I have Frequently Asked Questions TCP / IP.NET Sockets in my blog, which addresses exactly this question and has some sample code using my personal default setting for generating messages (byte length prefix is ββ4 bytes long).
4) How to enable the writeasync method so that I can send data through a socket in the middle of reading.
This is surprisingly complicated:
public static Task<int> SendTaskAsync(this Socket socket, byte[] buffer, int offset, int size, SocketFlags flags) { return Task<int>.Factory.FromAsync(socket.BeginSend, socket.EndSend, buffer, offset, size, flags, null); } public static Task WriteAsync(this Socket socket, byte[] buffer) { int bytesSent = 0; while (bytesSent != buffer.Length) { bytesSent += await socket.SendTaskAsync(data, bytesSent, buffer.Length - bytesSent, SocketFlags.None); } }