ソケットを使ってクライアントサーバプログラムを作成するための C# ライブラリ
Revisão | 9a76071abc774fa4353d49f89c276fe25491f5fb (tree) |
---|---|
Hora | 2015-08-14 23:30:40 |
Autor | tsntsumi <tsntsumi@tsnt...> |
Commiter | tsntsumi |
TcpConnection と TcpServer の共通データを独立させる修正
TcpConnection 内部で使用する HeaderLength, FooterLength,
ObtainPayloadLength は、生成する際にオプションとして引数で指
定可能としている。TcpServer では、パケットの受信に
TcpConnection を生成して使用するため、同様のデータをオプショ
ン引数で指定しなければならなかった。そのため、デフォルトの値
を二重に管理しなければならなかった。
そこで、デフォルトの値を、新規に作成した Packet クラスのオブ
ジェクトで管理するように変更した。
@@ -106,8 +106,7 @@ namespace SampleChatClient | ||
106 | 106 | RemoteIPAddress = serverIPAddress; |
107 | 107 | RemotePort = serverPort; |
108 | 108 | client.Connect(RemoteIPAddress, RemotePort); |
109 | - Connection = new TcpConnection(client.Client, | |
110 | - ChatMessage.HeaderLength, ChatMessage.FooterLength, ChatMessage.ObtainPayloadLength); | |
109 | + Connection = new TcpConnection(client.Client, new ChatMessage()); | |
111 | 110 | Connection.DataReceived += OnDataReceived; |
112 | 111 | } |
113 | 112 | } |
@@ -8,10 +8,9 @@ namespace SampleChatClient | ||
8 | 8 | /// <summary> |
9 | 9 | /// <see cref="ChatServer"/> と <see cref="ChatClient"/> 間で送受信されるチャットメッセージを表します。 |
10 | 10 | /// </summary> |
11 | - public class ChatMessage | |
11 | + public class ChatMessage: Packet | |
12 | 12 | { |
13 | - public const int HeaderLength = 2; | |
14 | - public const int FooterLength = 0; | |
13 | + public override int HeaderLength { get { return 2; } } | |
15 | 14 | |
16 | 15 | /// <summary> |
17 | 16 | /// メッセージを送受信する接続を取得または設定します。 |
@@ -26,6 +25,13 @@ namespace SampleChatClient | ||
26 | 25 | /// <summary> |
27 | 26 | /// コンストラクタ。 |
28 | 27 | /// </summary> |
28 | + public ChatMessage() | |
29 | + { | |
30 | + } | |
31 | + | |
32 | + /// <summary> | |
33 | + /// コンストラクタ。 | |
34 | + /// </summary> | |
29 | 35 | /// <param name="message">メッセージ文字列。</param> |
30 | 36 | /// <param name="connection">接続。</param> |
31 | 37 | public ChatMessage(string message, TcpConnection connection = null) |
@@ -39,7 +45,7 @@ namespace SampleChatClient | ||
39 | 45 | /// </summary> |
40 | 46 | /// <returns>ペイロード長。</returns> |
41 | 47 | /// <param name="header">ヘッダ。</param> |
42 | - public static int ObtainPayloadLength(byte[] header) | |
48 | + public override int ObtainPayloadLength(byte[] header) | |
43 | 49 | { |
44 | 50 | int payloadLength = header[0] << 8 | header[1]; |
45 | 51 |
@@ -53,14 +59,15 @@ namespace SampleChatClient | ||
53 | 59 | /// <param name="data">バイト配列データ。</param> |
54 | 60 | public static ChatMessage FromByteArray(byte[] data) |
55 | 61 | { |
62 | + ChatMessage chatMessage = new ChatMessage(); | |
56 | 63 | Encoding encoding = new UTF8Encoding(false); |
57 | 64 | // int length = (data[0] << 8) | data[1]; |
58 | - byte[] messageData = new byte[data.Length - HeaderLength]; | |
65 | + byte[] messageData = new byte[data.Length - chatMessage.HeaderLength]; | |
59 | 66 | |
60 | - Array.Copy(data, HeaderLength, messageData, 0, messageData.Length); | |
61 | - string message = encoding.GetString(messageData); | |
67 | + Array.Copy(data, chatMessage.HeaderLength, messageData, 0, messageData.Length); | |
68 | + chatMessage.Message = encoding.GetString(messageData); | |
62 | 69 | |
63 | - return new ChatMessage(message); | |
70 | + return chatMessage; | |
64 | 71 | } |
65 | 72 | |
66 | 73 | /// <summary> |
@@ -41,8 +41,7 @@ namespace SampleChatServer | ||
41 | 41 | /// <param name="port">待ち受けするポート番号。</param> |
42 | 42 | public ChatServer (IPAddress ipAddress, int port) |
43 | 43 | { |
44 | - tcpServer = new TcpServer(ipAddress, port, | |
45 | - ChatMessage.HeaderLength, ChatMessage.FooterLength, ChatMessage.ObtainPayloadLength); | |
44 | + tcpServer = new TcpServer(ipAddress, port, new ChatMessage()); | |
46 | 45 | tcpServer.Connected += OnConnected; |
47 | 46 | tcpServer.Disconnected += OnDisconnected; |
48 | 47 | tcpServer.DataReceived += OnDataReceived; |
@@ -0,0 +1,27 @@ | ||
1 | +using System; | |
2 | + | |
3 | +namespace SocketNet | |
4 | +{ | |
5 | + public class Packet | |
6 | + { | |
7 | + public virtual int HeaderLength { get { return 4; } } | |
8 | + public virtual int FooterLength { get { return 0; } } | |
9 | + | |
10 | + public virtual int ObtainPayloadLength(byte[] header) | |
11 | + { | |
12 | + int payloadLength = 0; | |
13 | + | |
14 | + if (header.Length < HeaderLength) | |
15 | + { | |
16 | + return 0; | |
17 | + } | |
18 | + for (int i = 0; i < HeaderLength; i++) | |
19 | + { | |
20 | + payloadLength = (payloadLength << 8) | header[i]; | |
21 | + } | |
22 | + | |
23 | + return payloadLength; | |
24 | + } | |
25 | + } | |
26 | +} | |
27 | + |
@@ -36,6 +36,7 @@ | ||
36 | 36 | <Compile Include="TcpConnectionEventArgs.cs" /> |
37 | 37 | <Compile Include="TcpDataReceivedEventArgs.cs" /> |
38 | 38 | <Compile Include="TcpServer.cs" /> |
39 | + <Compile Include="Packet.cs" /> | |
39 | 40 | </ItemGroup> |
40 | 41 | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
41 | 42 | </Project> |
\ No newline at end of file |
@@ -32,9 +32,7 @@ namespace SocketNet | ||
32 | 32 | public sealed class TcpConnection: IDisposable |
33 | 33 | { |
34 | 34 | private static readonly int ReceivedDataBufferSize = 65535; |
35 | - private readonly int PacketHeaderLength; | |
36 | - private readonly int PacketFooterLength; | |
37 | - private readonly ObtainPayloadLengthDelegate ObtainPayloadLength; | |
35 | + private readonly Packet Packet = new Packet(); | |
38 | 36 | |
39 | 37 | private object syncObject; |
40 | 38 | private NetworkStream networkStream; |
@@ -111,14 +109,8 @@ namespace SocketNet | ||
111 | 109 | /// コンストラクタ。 |
112 | 110 | /// </summary> |
113 | 111 | /// <param name="client">ソケット。</param> |
114 | - /// <param name="headerLength">パケットヘッダ長。</param> | |
115 | - /// <param name="footerLength">パケットフッダ長。</param> | |
116 | - /// <param name="obtainPayloadLength">ペイロード長取得デリゲート。</param> | |
117 | - public TcpConnection( | |
118 | - Socket client, | |
119 | - int headerLength = 4, | |
120 | - int footerLength = 0, | |
121 | - ObtainPayloadLengthDelegate obtainPayloadLength = null) | |
112 | + /// <param name="packet">受信するパケットの各部の長さの設定を格納するオブジェクト。</param> | |
113 | + public TcpConnection(Socket client, Packet packet) | |
122 | 114 | { |
123 | 115 | syncObject = new object(); |
124 | 116 | Client = client; |
@@ -126,50 +118,17 @@ namespace SocketNet | ||
126 | 118 | Reader = new BinaryReader(networkStream); |
127 | 119 | Writer = new BinaryWriter(networkStream); |
128 | 120 | |
129 | - PacketHeaderLength = headerLength; | |
130 | - PacketFooterLength = footerLength; | |
131 | - | |
132 | - if (obtainPayloadLength == null) | |
133 | - { | |
134 | - ObtainPayloadLength = ObtainPayloadLengthFromHeader; | |
135 | - } | |
136 | - else | |
137 | - { | |
138 | - ObtainPayloadLength = obtainPayloadLength; | |
139 | - } | |
121 | + Packet = packet; | |
140 | 122 | |
141 | 123 | partiallyReceivedData = new byte[ReceivedDataBufferSize]; |
142 | 124 | completelyReceivedData = new List<byte>(); |
143 | - header = new byte[headerLength]; | |
125 | + header = new byte[Packet.HeaderLength]; | |
144 | 126 | packet = null; |
145 | 127 | packetPayloadLength = 0; |
146 | 128 | bytesToProcessRemaining = 0; |
147 | 129 | } |
148 | 130 | |
149 | 131 | /// <summary> |
150 | - /// パケットのペイロード長を、ヘッダから計算して返します | |
151 | - /// </summary> | |
152 | - /// <returns>ペイロード長。</returns> | |
153 | - /// <param name="header">ヘッダを含む配列。</param> | |
154 | - /// <remarks> | |
155 | - /// ヘッダの先頭に4バイトのペイロード長があるとして、int 型に変換して返します。 | |
156 | - /// </remarks> | |
157 | - private int ObtainPayloadLengthFromHeader(byte[] header) | |
158 | - { | |
159 | - if (header.Length < 4) | |
160 | - { | |
161 | - return 0; | |
162 | - } | |
163 | - int payloadLength = 0; | |
164 | - for (int i = 0; i < 4; i++) | |
165 | - { | |
166 | - payloadLength = (payloadLength << 8) | header[i]; | |
167 | - } | |
168 | - | |
169 | - return payloadLength; | |
170 | - } | |
171 | - | |
172 | - /// <summary> | |
173 | 132 | /// 関連付けられたリソースを解放します。 |
174 | 133 | /// </summary> |
175 | 134 | public void Dispose() |
@@ -278,14 +237,14 @@ namespace SocketNet | ||
278 | 237 | switch (receiveState) |
279 | 238 | { |
280 | 239 | case DataReceiveState.PacketHeader: |
281 | - if (completelyReceivedData.Count >= PacketHeaderLength) | |
240 | + if (completelyReceivedData.Count >= Packet.HeaderLength) | |
282 | 241 | { |
283 | - completelyReceivedData.CopyTo(0, header, 0, PacketHeaderLength); | |
284 | - packetPayloadLength = ObtainPayloadLength(header); | |
242 | + completelyReceivedData.CopyTo(0, header, 0, Packet.HeaderLength); | |
243 | + packetPayloadLength = Packet.ObtainPayloadLength(header); | |
285 | 244 | |
286 | 245 | receiveState = DataReceiveState.PacketPayload; |
287 | - completelyReceivedData.RemoveRange(0, PacketHeaderLength); | |
288 | - bytesToProcessRemaining -= PacketHeaderLength; | |
246 | + completelyReceivedData.RemoveRange(0, Packet.HeaderLength); | |
247 | + bytesToProcessRemaining -= Packet.HeaderLength; | |
289 | 248 | } |
290 | 249 | else |
291 | 250 | { |
@@ -296,15 +255,15 @@ namespace SocketNet | ||
296 | 255 | case DataReceiveState.PacketPayload: |
297 | 256 | if (completelyReceivedData.Count >= packetPayloadLength) |
298 | 257 | { |
299 | - packet = new byte[PacketHeaderLength + packetPayloadLength + PacketFooterLength]; | |
300 | - Array.Copy(header, packet, PacketHeaderLength); | |
301 | - completelyReceivedData.CopyTo(0, packet, PacketHeaderLength, packetPayloadLength); | |
258 | + packet = new byte[Packet.HeaderLength + packetPayloadLength + Packet.FooterLength]; | |
259 | + Array.Copy(header, packet, Packet.HeaderLength); | |
260 | + completelyReceivedData.CopyTo(0, packet, Packet.HeaderLength, packetPayloadLength); | |
302 | 261 | |
303 | 262 | receiveState = DataReceiveState.PacketFooter; |
304 | 263 | completelyReceivedData.RemoveRange(0, packetPayloadLength); |
305 | 264 | bytesToProcessRemaining -= packetPayloadLength; |
306 | 265 | |
307 | - if (PacketFooterLength == 0) | |
266 | + if (Packet.FooterLength == 0) | |
308 | 267 | { |
309 | 268 | OnDataReceived(new TcpDataReceivedEventArgs(this, packet)); |
310 | 269 | receiveState = DataReceiveState.PacketHeader; |
@@ -317,15 +276,15 @@ namespace SocketNet | ||
317 | 276 | break; |
318 | 277 | |
319 | 278 | case DataReceiveState.PacketFooter: |
320 | - if (completelyReceivedData.Count >= PacketFooterLength) | |
279 | + if (completelyReceivedData.Count >= Packet.FooterLength) | |
321 | 280 | { |
322 | - completelyReceivedData.CopyTo(0, packet, PacketHeaderLength + packetPayloadLength, PacketFooterLength); | |
281 | + completelyReceivedData.CopyTo(0, packet, Packet.HeaderLength + packetPayloadLength, Packet.FooterLength); | |
323 | 282 | |
324 | 283 | OnDataReceived(new TcpDataReceivedEventArgs(this, packet)); |
325 | 284 | |
326 | 285 | receiveState = DataReceiveState.PacketHeader; |
327 | - completelyReceivedData.RemoveRange(0, PacketFooterLength); | |
328 | - bytesToProcessRemaining -= PacketFooterLength; | |
286 | + completelyReceivedData.RemoveRange(0, Packet.FooterLength); | |
287 | + bytesToProcessRemaining -= Packet.FooterLength; | |
329 | 288 | } |
330 | 289 | else |
331 | 290 | { |
@@ -38,9 +38,7 @@ namespace SocketNet | ||
38 | 38 | /// </summary> |
39 | 39 | public static readonly int MaxPendingConnections = 3; |
40 | 40 | |
41 | - private readonly int PacketHeaderLength; | |
42 | - private readonly int PacketFooterLength; | |
43 | - private readonly TcpConnection.ObtainPayloadLengthDelegate ObtainPayloadLength; | |
41 | + private readonly Packet Packet; | |
44 | 42 | |
45 | 43 | private TcpListener tcpListener; |
46 | 44 | private List<TcpConnection> clientConnections; |
@@ -115,18 +113,12 @@ namespace SocketNet | ||
115 | 113 | /// コンストラクタ。 |
116 | 114 | /// </summary> |
117 | 115 | /// <param name="port">バインドするポート。</param> |
118 | - /// <param name="headerLength">ヘッダの長さ。</param> | |
119 | - /// <param name="footerLength">フッタの長さ。</param> | |
120 | - /// <param name="obtainPayloadLength">ペイロード長取得デリゲート。</param> | |
116 | + /// <param name="packet">受信するパケットの各部の長さの設定を格納するオブジェクト。</param> | |
121 | 117 | /// <remarks> |
122 | 118 | /// ループバックアドレスを使用します。 |
123 | 119 | /// </remarks> |
124 | - public TcpServer( | |
125 | - int port, | |
126 | - int headerLength = 4, | |
127 | - int footerLength = 0, | |
128 | - TcpConnection.ObtainPayloadLengthDelegate obtainPayloadLength = null) | |
129 | - : this(IPAddress.Loopback, port, headerLength, footerLength, obtainPayloadLength) | |
120 | + public TcpServer(int port, Packet packet) | |
121 | + : this(IPAddress.Loopback, port, packet) | |
130 | 122 | { |
131 | 123 | } |
132 | 124 |
@@ -135,22 +127,12 @@ namespace SocketNet | ||
135 | 127 | /// </summary> |
136 | 128 | /// <param name="ipAddress">バインドするIPアドレス。</param> |
137 | 129 | /// <param name="port">バインドするポート。</param> |
138 | - /// <param name="headerLength">ヘッダの長さ。</param> | |
139 | - /// <param name="footerLength">フッタの長さ。</param> | |
140 | - /// <param name="obtainPayloadLength">ペイロード長取得デリゲート。</param> | |
141 | - public TcpServer( | |
142 | - IPAddress ipAddress, | |
143 | - int port, | |
144 | - int headerLength = 4, | |
145 | - int footerLength = 0, | |
146 | - TcpConnection.ObtainPayloadLengthDelegate obtainPayloadLength = null) | |
130 | + /// <param name="packet">受信するパケットの各部の長さの設定を格納するオブジェクト。</param> | |
131 | + public TcpServer(IPAddress ipAddress, int port, Packet packet) | |
147 | 132 | { |
148 | - PacketHeaderLength = headerLength; | |
149 | - PacketFooterLength = footerLength; | |
150 | - ObtainPayloadLength = obtainPayloadLength; | |
151 | - | |
152 | - Port = port; | |
153 | 133 | IPAddress = ipAddress; |
134 | + Port = port; | |
135 | + Packet = packet; | |
154 | 136 | clientConnections = new List<TcpConnection>(); |
155 | 137 | connectionsToClose = new List<TcpConnection>(); |
156 | 138 | isShuttingDown = false; |
@@ -231,8 +213,7 @@ namespace SocketNet | ||
231 | 213 | |
232 | 214 | await Task.Run(() => |
233 | 215 | { |
234 | - TcpConnection connection = new TcpConnection( | |
235 | - socket, PacketHeaderLength, PacketFooterLength, ObtainPayloadLength); | |
216 | + TcpConnection connection = new TcpConnection(socket, Packet); | |
236 | 217 | connection.Disconnected += new EventHandler<TcpConnectionEventArgs>(OnDisconnected); |
237 | 218 | connection.DataReceived += new EventHandler<TcpDataReceivedEventArgs>(OnDataReceived); |
238 | 219 |