I recently posted a question about improving thread and socket safety in my NetworkEndpoint class. previous post
I have implemented the changes suggested in the answer I got there. When I asked for a second look to confirm my changes I was told to post another question.
Included below is the code I changed as well as a few follow up questions. To see the entire class please refer to my previous post.
- Is my use of locks in the Connected property and Send and Disconnect methods effective?
private object connectedLock = new object();
public bool Connected {
get {
lock (connectedLock) {
if (connection == null) return false;
return connection.Connected;
}
}
}
private object sendLock = new object();
public void Send(NetworkHeader header, byte[] data) {
if (!Connected) throw new InvalidOperationException("NetworkEndpoint must be connected before sending.");
try {
lock (sendLock) {
connection.Send(ByteUtils.Combine(header.Serialize(), data));
}
} catch (SocketException) {
Disconnect();
}
}
private object disconnectLock = new object();
public void Disconnect() {
if (Connected) {
lock (disconnectLock) {
connection?.Shutdown(SocketShutdown.Both);
connection?.Close();
connection = null;
OnDisconnected();
Clear();
}
}
}
- Is the lock I added to InitializeReceiveLoop redundant due to
if (Receiving) return;?
private object initializeLock = new object();
public void InitializeReceiveLoop() {
lock (initializeLock) {
if (Receiving) return;
Receiving = true;
}
BeginReceive();
}
- Is this the best way to clear events? Would setting the events to null achieve the same goal?
public void Clear() {
foreach (Delegate d in DataReceived.GetInvocationList())
DataReceived -= (EventHandler<NetworkReceiveEventArgs>)d;
foreach (Delegate d in Disconnected.GetInvocationList())
Disconnected -= (EventHandler)d;
}
- Is this implementation of EndReceive better than my previous?
- Is EndReceive not already "thread-safe" by nature of how it is used? It is a private method only ever called by BeginReceive which is only called after the completion of a previous call to EndReceive or by InitializeReceiveLoop which can only be called once (returns imediately if Receiving is already true)
private void EndReceive(IAsyncResult result) {
byte[] dataBuffer = null;
NetworkHeader header = null;
try {
if (connection.EndReceive(result) > 0) {
header = NetworkHeader.Deserialize(headBuffer);
dataBuffer = new byte[header.DataSize];
int offset = 0;
while (offset < header.DataSize) {
int lengthRead = connection.Receive(dataBuffer, offset,
(int)header.DataSize - offset, SocketFlags.None);
if (lengthRead == 0) {
Disconnect();
return;
}
else offset += lengthRead;
}
}
} catch (SocketException) {
Disconnect();
return;
}
OnDataReceived(header, dataBuffer);
BeginReceive();
}