I want to constantly transfer from the server to the client an array of bytes, but it turns out to transfer only 1 time. I do not understand why.

Server:

public ServerSocket(IPAddress ipAddress, int port) { _clients = new List <ClientModel> (); _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _server.Bind(new IPEndPoint(ipAddress, port)); _server.Listen(20); Accept(); } private async void Accept() { for (int i = 0; i < 10; i++) { var socket = await Task.Factory.FromAsync<Socket>(_server.BeginAccept, _server.EndAccept, true); var newClient = new ClientModel { Id = _id++, ClientSocket = socket }; _clients.Add(newClient); await Send(1, "hello " + i + ""); } } public async Task Send(int id, String data) { var client = _clients.FirstOrDefault(cl => cl.Id == id); if (client == null) return; var byteData = Encoding.UTF8.GetBytes(data); await Task.Factory.FromAsync<int>( client.ClientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, null, client.ClientSocket), client.ClientSocket.EndSend); } 

Customer:

 public ClientSocket() { _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } public bool Connect(IPAddress ipAddress, int port) { _client.Connect(ipAddress, port); if (!_client.Connected) { return false; } Receive(); return true; } private async void Receive() { var buffer = new byte[4096]; for (;;) { try { var bytesRead = await Task.Factory.FromAsync<int>( _client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, null, _client), _client.EndReceive); if (bytesRead <= 0) continue; var message = Encoding.UTF8.GetString(buffer, 0, bytesRead); Console.WriteLine(message); } catch { break; } } 

It is necessary to take into account that there can be several simultaneous clients. The problem is that on the 2nd iteration the Server waits on "BeginAccept / EndAccept", and the client on "BeginReceive / EndReceive". Ie it turns out that the client is ready to receive, but the server does not know about it. Tell me how to solve the problem. Interesting fact: If you start the server, then the client, then the array will be transferred once. But if (without closing anything) open a new client instance and execute it, then the first instance is an example of the 2nd array. If the second instance of the client is executed again, then the first will accept the third array. And so on

    1 answer 1

    There are a lot of mistakes and shortcomings:

    1. Why your example does not work: await Send(1, "hello " + i + ""); must be replaced with await Send(i, "hello " + i + ""); since otherwise we will send only to the first client.
    2. Together with await, in this case, ConfigureAwait (false) must be set so that the answer does not come to the UI. If any of the calling functions need to be moved to the UI, it can do it.
    3. catch without logging will lead to subtle errors. You should always at least write to the log.
    4. Do not use async void. In this case, the exception in .Net 4.0 will fill up the application, and in .Net 4.5 it will simply be lost.
    5. Instead of _id++ in this case, it is better to use Interlocked.Increment , since the application is already trying to be multi-threaded.
    6. Instead of using the Socket class, it is more logical to use TcpClient. It has more amenities.
    7. Instead of Console.WriteLine it is better to use await Console.Out.WriteLineAsync. It is asynchronous, writes to the correct stream (since Out and Err must be separated, as this is a different pipeline).
    8. In the client, instead of quitting the exception, it is better to check whether there is a connection or not.
    9. Both the server and clients must have Dispose methods.
    10. In the current example, the client never stops. I added a stop by line break ( \n )

    So in order to just run your example, you need to remove that very unit, see the first paragraph. However, I rewrote the code, all of a sudden someone needs another example of client-server communication :-) It also has shortcomings, for the sake of simplifying the code, but they are smaller.

     internal static class MainClass { private const int Port = 9635; public static void Main() { AsyncMain().Wait(); } private static async Task AsyncMain() { using (var server = new ServerSocket(IPAddress.Loopback, Port)) { var serverTask = server.Start(); var clients = Enumerable.Repeat(0, 10).Select(_ => new ClientSocket()).ToArray(); var connectionTasks = clients.Select(c => c.ConnectAsync(IPAddress.Loopback, Port)).ToArray(); await Task.WhenAll(connectionTasks).ConfigureAwait(false); Array.ForEach(clients, c => c.Dispose()); await serverTask.ConfigureAwait(false); } Console.ReadKey(); } } internal sealed class ClientModel { public ClientModel(int id, Socket clientSocket) { // use nuget package 'CheckContracts' Validate.ArgumentGreaterOrEqualThanZero(id, nameof(id)); Validate.ArgumentIsNotNull(clientSocket, nameof(clientSocket)); Id = id; ClientSocket = clientSocket; } public int Id { get; } public Socket ClientSocket { get; } } internal sealed class ServerSocket : IDisposable { private readonly List<ClientModel> _clients; private readonly Socket _server; private int _id; public ServerSocket(IPAddress ipAddress, int port) { _clients = new List<ClientModel>(); _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _server.Bind(new IPEndPoint(ipAddress, port)); _server.Listen(20); } public async Task Start() { for (var i = 0; i < 10; i++) { var socket = await Task.Factory.FromAsync(_server.BeginAccept, _server.EndAccept, true).ConfigureAwait(false); var newClient = new ClientModel(Interlocked.Increment(ref _id), socket); _clients.Add(newClient); await SendAsync(newClient.Id, "hello " + newClient.Id + "\n").ConfigureAwait(false); } } public async Task SendAsync(int id, string data) { var client = _clients.FirstOrDefault(cl => cl.Id == id); Validate.IsNotNull(client, $"Unable to find client with id {id}"); var byteData = Encoding.UTF8.GetBytes(data); await Task.Factory.FromAsync( client.ClientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, null, client.ClientSocket), client.ClientSocket.EndSend).ConfigureAwait(false); } public void Dispose() { if (!ReferenceEquals(_server, null)) { _server.Dispose(); } if (!ReferenceEquals(_clients, null)) { foreach (var clientModel in _clients) { clientModel.ClientSocket.Dispose(); } } } } internal sealed class ClientSocket : IDisposable { private readonly Socket _client; public ClientSocket() { _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } public async Task<bool> ConnectAsync(IPAddress ipAddress, int port) { _client.Connect(ipAddress, port); if (!_client.Connected) { return false; } await ReceiveAsync().ConfigureAwait(false); return true; } private async Task ReceiveAsync() { var buffer = new byte[4096]; for (;;) { try { if (!_client.Connected) { return; } var bytesRead = await Task.Factory.FromAsync( _client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, null, _client), _client.EndReceive).ConfigureAwait(false); if (bytesRead <= 0) { continue; } var message = Encoding.UTF8.GetString(buffer, 0, bytesRead); await Console.Out.WriteLineAsync(message).ConfigureAwait(false); if (message.EndsWith("\n")) { return; } } catch(Exception ex) { Console.Error.WriteLine(ex); break; } } } public void Dispose() { if (!ReferenceEquals(_client, null)) { _client.Dispose(); } } } 
    • +1, but for my taste Array.ForEach(clients, c => c.Dispose()); read much worse than foreach (var c in clients) c.Dispose(); - VladD
    • Yes, it is possible. Here, I would advise using CompositeDisposable ( msdn.microsoft.com/en-us/library/… ) in general, but to drag a large number of dependencies in response would not be a good idea. - Manushin Igor
    • Wow, did not know about CompositeDisposable , I am adopting. Thank! - VladD