Send / receive wrapper for System.Net.Sockets - follow-up
Events can be cleared much simpler:


public void Clear() {
    foreach (Delegate d in DataReceived.GetInvocationList())
        DataReceived -= (EventHandler&lt;NetworkReceiveEventArgs&gt;)d;
    foreach (Delegate d in Disconnected.GetInvocationList())
        Disconnected -= (EventHandler)d;
}



public void Clear() {
    DataReceived = null;
    Disconnected = null;
}




This is by no means a full review of your code, but I would like to explain a couple of things to you.

You are using seperate locks for sending, checking connection status, initializing and disconnecting. Think about the implications by using a seperate lock for operations that all require a common thread-aware resource Connected. Since they all take a different lock, they can simultaniously change the state and corrupt the other ongoing operation. This is why you should use a single lock for all these operations.

private object syncRoot = new object();


But even then, since you check the condition before the lock, data integrity might still be breached by an unfortunate flow of events. 

public void Send(NetworkHeader header, byte[] data) {
    if (!Connected) // ..      // &lt;- outside lock
    lock (syncRoot) {          // &lt;- inside lock, but condition might no longer be true
        // ..
    }
}


As an example, suppose two threads simultaniously call respectively Send and Disconnect. A possible flow is:

- thread A: call Send                    // thread A is first
- thread A: check IsConnected: true
- thread B: call Disconnect              // thread B starts a fraction later
- thread B: check IsConnected: true
- thread B: take lock
- thread B: set IsConnected: false
- thread B: release lock
- thread A: take lock                    // thread A reaches a point where it 
             // expected its state to still be valid, but thread B changed it
- thread A: connection.Send(..)          // &lt;- null-reference exception


There used to be a recommedation about double-checked locking. But here is a good reason not to use it. So we will have to take the condition inside the lock.

public void Send(NetworkHeader header, byte[] data) {
    lock (syncRoot) { 
        if (!Connected) // ..   
        // ..
    }
}


Moving on, we can change Connected to take a volatile boolean, so we don't require additional locking when just getting its value. Just make sure to set its value when connection is established and reset on Disconnect. The volatile field is read as an atomic operation. You get the actual value, rather than a cached value (which might be done to optimize state in a non-concurrent context).

private volatile bool _connected = false;

public bool Connected =&gt; _connected;   // &lt;- no locking required


I hope this explains some basic concepts of locks and thread-safety.