I'd like to get a little feedback on this async socket wrapper. My goal is to merge socket Begin/End methods into a single async call. I also wanted to wrap exceptions so they are easier to handle in others areas of my program. Any red flags? Any potential problems? I know its not doing much but I want to get this right.
public class SimpleSocket
{
private readonly Socket _socket;
public SimpleSocket()
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public SimpleSocket(Socket socket)
{
_socket = socket;
}
public void Bind(int port)
{
var endPoint = new IPEndPoint(IPAddress.Any, port);
try
{
_socket.Bind(endPoint);
}
catch (Exception e)
{
throw new ConnectionErrorException($"Failed to bind to {port}", e);
}
}
public void Listen(int backlog)
{
try
{
_socket.Listen(backlog);
}
catch (Exception e)
{
throw new ConnectionErrorException($"Failed to listen with backlog of {backlog}", e);
}
}
public async Task<SimpleSocket> AcceptAsync()
{
Socket socket;
try
{
socket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, true);
}
catch (Exception e)
{
throw new ConnectionErrorException("Failed to accept connection", e);
}
return new SimpleSocket(socket);
}
public async Task ConnectAsync(string host, int port)
{
try
{
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, host, port, null);
}
catch (Exception e)
{
throw new ConnectionErrorException($"Failed to connect to {host}:{port}", e);
}
}
public async Task<int> ReceiveAsync(byte[] buffer, int offset, int size)
{
int bytesReceived;
try
{
bytesReceived = await Task<int>.Factory.FromAsync(
_socket.BeginReceive(buffer, offset, size, SocketFlags.None, null, null),
_socket.EndReceive);
}
catch (Exception e)
{
if (e is SocketException se && se.SocketErrorCode == SocketError.ConnectionReset)
{
throw new ConnectionClosedException("Connection reset");
}
throw new ConnectionErrorException("Failed to receieve message", e);
}
if (bytesReceived == 0)
{
throw new ConnectionClosedException("Connection closed");
}
return bytesReceived;
}
public async Task<int> SendAsync(byte[] buffer, int offset, int size)
{
try
{
return await Task<int>.Factory.FromAsync(
_socket.BeginSend(buffer, offset, size, SocketFlags.None, null, null),
_socket.EndSend);
}
catch (Exception e)
{
throw new ConnectionErrorException("Failed to send message", e);
}
}
public void Close()
{
_socket.Close();
}
}
EDIT: Maybe I'll highlight some parts I'm concerned about. Am I doing the Task.Factory.FromAsync right? Especially for ReceiveAsync and SendAsync? I saw a strange example that looked like this:
var revcLen = await Task.Factory.FromAsync(
(cb, s) => clientSocket.BeginReceive(prefix, 0, prefix.Length, SocketFlags.None, cb, s),
ias => clientSocket.EndReceive(ias),
null);
Why the extra lambda expression? It seems like they are just forcing the usage of one overload while another would be better suited.
Also, do you guys think I should just return zero bytes from ReceiveAsync instead of throwing ConnectionClosedException? Then I can just let the exceptions flow from the socket methods and wrap them in a higher layer. I have a connection abstraction that handles message framing and eventually keep alives.
NetworkStreamhasReadAsyncandWriteAsyncmethods. \$\endgroup\$SocketimplementsIDisposabletherefore your class should as well and properly implement the Disposable pattern. \$\endgroup\$