図 2 クライアントとサーバーのための標準的な Socket 呼び出し
|
サーバー | クライアント |
Socket.Socket | Socket.Socket |
Socket.Bind | Socket.Bind (オプション) |
Socket.Listen | Socket.Connect |
Socket.Accept | |
Socket.Read/Socket.Write | Socket.Read/Socket.Write |
Socket.Shutdown (オプション) | Socket.Shutdown (オプション) |
Socket.Close | Socket.Close |
図 3 重要なエラー コード
|
エラー番号 | Winsock の値 | SocketError の値 | 意味 |
10004 | WSAEINTR | Interrupted | システム呼び出しが中断されました。ソケット呼び出しを実行中で、ソケットが閉じられた時に、この状況が発生します。 |
10048 | WSAEADDRINUSE | AddressAlreadyInUse | 結合または聞き入れようとしているアドレスは、使用中です。 |
10053 | WSACONNABORTED | ConnectionAborted | 接続がローカルで中断されました。 |
10054 | WSAECONNRESET | ConnectionReset | 接続は、他のエンドによってリセットされました。 |
10061 | WSAECONNREFUSED | ConnectionRefused | リモート ホストによって、接続が拒否されました。リモート ホストが起動していないか、ビジーでリスニング キューが一杯なために接続を受け入れられない場合に発生します。 |
図 4 サーバー ソケットの設定
|
private Socket _serverSocket;
private void SetupServerSocket()
{
// ローカル マシンの情報を解析します。
IPHostEntry localMachineInfo =
Dns.GetHostEntry(Dns.GetHostName());
IPEndPoint myEndpoint = new IPEndPoint(
localMachineInfo.AddressList[0], _port);
// ソケットを作成し、結合し、聞き取りを開始します。
_serverSocket = new Socket(myEndpoint.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(myEndpoint);
_serverSocket.Listen((int)SocketOptionName.MaxConnections);
}
図 5 単純なスレッデッド サーバー
|
class ThreadedServer
{
private Socket _serverSocket;
private int _port;
public ThreadedServer(int port) { _port = port; }
private class ConnectionInfo
{
public Socket Socket;
public Thread Thread;
}
private Thread _acceptThread;
private List<ConnectionInfo> _connections =
new List<ConnectionInfo>();
public void Start()
{
SetupServerSocket();
_acceptThread = new Thread(AcceptConnections);
_acceptThread.IsBackground = true;
_acceptThread.Start();
}
private void SetupServerSocket()
{
// ローカル マシンの情報を解析します。
IPHostEntry localMachineInfo =
Dns.GetHostEntry(Dns.GetHostName());
IPEndPoint myEndpoint = new IPEndPoint(
localMachineInfo.AddressList[0], _port);
// ソケットを作成し、結合し、聞き取りを開始します。
_serverSocket = new Socket(myEndpoint.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(myEndpoint);
_serverSocket.Listen((int)SocketOptionName.MaxConnections);
}
private void AcceptConnections()
{
while (true)
{
// 接続を受け入れます。
Socket socket = _serverSocket.Accept();
ConnectionInfo connection = new ConnectionInfo();
connection.Socket = socket;
// 受信用スレッドを作成します。
connection.Thread = new Thread(ProcessConnection);
connection.Thread.IsBackground = true;
connection.Thread.Start(connection);
// ソケットを保存します。
lock (_connections) _connections.Add(connection);
}
}
private void ProcessConnection(object state)
{
ConnectionInfo connection = (ConnectionInfo)state;
byte[] buffer = new byte[255];
try
{
while (true)
{
int bytesRead = connection.Socket.Receive(buffer);
if (bytesRead > 0)
{
lock (_connections)
{
foreach (ConnectionInfo conn in _connections)
{
if (conn != connection)
{
conn.Socket.Send(
buffer, bytesRead, SocketFlags.None);
}
}
}
}
else if (bytesRead == 0) return;
}
}
catch (SocketException exc)
{
Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
Console.WriteLine("Exception: " + exc);
}
finally
{
connection.Socket.Close();
lock (_connections) _connections.Remove(connection);
}
}
}
図 6 Select ベース サーバー
|
class SelectBasedServer
{
... // ThreadedServer と SetupServerSocket は同じ要因です。
public void Start()
{
Thread selectThread = new Thread(ProcessSockets);
selectThread.IsBackground = true;
selectThread.Start();
}
private void ProcessSockets()
{
byte[] buffer = new byte[255];
List<Socket> readSockets = new List<Socket>();
List<Socket> connectedSockets = new List<Socket>();
try
{
SetupServerSocket();
while (true)
{
// 読み出しリストを満たします。
readSockets.Clear();
readSockets.Add(_serverSocket);
readSockets.AddRange(connectedSockets);
// 何か実行することを待ちます。
Socket.Select(readSockets, null, null, int.MaxValue);
// 何か実行することを持つ各ソケットを処理します
foreach (Socket readSocket in readSockets)
{
if (readSocket == _serverSocket)
{
// 新しいクライアントのソケットを受け入れて保存します。
Socket newSocket = readSocket.Accept();
connectedSockets.Add(newSocket);
}
else
{
// データを読み出し、適切に処理します。
int bytesRead = readSocket.Receive(buffer);
if (0 == bytesRead)
{
connectedSockets.Remove(readSocket);
readSocket.Close();
}
else
{
foreach (Socket connectedSocket in
connectedSockets)
{
if (connectedSocket != readSocket)
{
connectedSocket.Send(buffer,
bytesRead, SocketFlags.None);
}
}
}
}
}
}
}
catch (SocketException exc)
{
Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
Console.WriteLine("Exception: " + exc);
}
finally
{
foreach (Socket s in connectedSockets) s.Close();
connectedSockets.Clear();
}
}
}
図 7 非同期サーバー
|
class AsynchronousIoServer
{
... // ThreadedServer と SetupServerSocket は同じ要因です。
private class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
}
private List<ConnectionInfo> _connections =
new List<ConnectionInfo>();
public void Start()
{
SetupServerSocket();
for (int i = 0; i < 10; i++)
_serverSocket.BeginAccept(
new AsyncCallback(AcceptCallback), _serverSocket);
}
private void AcceptCallback(IAsyncResult result)
{
ConnectionInfo connection = new ConnectionInfo();
try
{
// Accpet を終了します。
Socket s = (Socket)result.AsyncState;
connection.Socket = s.EndAccept(result);
connection.Buffer = new byte[255];
lock (_connections) _connections.Add(connection);
// Receive と新しい Accept を開始します。
connection.Socket.BeginReceive(connection.Buffer, 0,
connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), connection);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback),
result.AsyncState);
}
catch (SocketException exc)
{
CloseConnection(connection);
Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
CloseConnection(connection);
Console.WriteLine("Exception: " + exc);
}
}
private void ReceiveCallback(IAsyncResult result)
{
ConnectionInfo connection = (ConnectionInfo)result.AsyncState;
try
{
int bytesRead = connection.Socket.EndReceive(result);
if (0 != bytesRead)
{
lock (_connections)
{
foreach (ConnectionInfo conn in _connections)
{
if (connection != conn)
{
conn.Socket.Send(connection.Buffer, bytesRead,
SocketFlags.None);
}
}
}
connection.Socket.BeginReceive(connection.Buffer, 0,
connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), connection);
}
else CloseConnection(connection);
}
catch (SocketException exc)
{
CloseConnection(connection);
Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
CloseConnection(connection);
Console.WriteLine("Exception: " + exc);
}
}
private void CloseConnection(ConnectionInfo ci)
{
ci.Socket.Close();
lock (_connections) _connections.Remove(ci);
}
}
図 8 構成ファイル
|
<configuration>
<system.diagnostics>
<sources>
<source name="System.Net.Sockets">
<listeners>
<add name="Sockets"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net.Sockets" value="31" />
</switches>
<sharedListeners>
<add name="Sockets" type="System.Diagnostics.TextWriterTraceListener"
initializeData="Sockets.log"/>
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
</configuration>