Alternate e697dbe9c5997e35395fe158628dd8c5209481da
for Visual Studio 2022 and Windows 11.
読み取り中…
検索中…
一致する文字列を見つけられません
SocketBuilder.cpp
[詳解]
1// ----------------------------------------------------------------------------
6
7#include "pch.h"
8#include "SocketBuilder.h"
9
10using namespace alt;
11
13{
14 ZeroMemory (&_SockAddrIn, sizeof (_SockAddrIn));
15 ZeroMemory (&_wsaData, sizeof (_wsaData));
16 _wsaEvent = WSA_INVALID_EVENT;
17}
18
20{
21 this->Close ();
22
23 ::WSACleanup ();
24}
25
27{
28 int ret = ::WSAStartup (MAKEWORD (2, 0), &_wsaData);
29 if (ret != 0)
30 {
31 ::WSACleanup ();
32 }
33
34 return ret;
35}
36
37BOOL SocketBuilder::Socket (int af, int type, int protocol)
38{
39 BOOL response = TRUE;
40
41 _socket = ::socket (af, type, protocol);
42 if (_socket == INVALID_SOCKET)
43 {
44 response = FALSE;
45 }
46
47 return response;
48}
49
51 ADDRESS_FAMILY family, u_short portNo, LPCTSTR lpctszAddress)
52{
53 BOOL response = TRUE;
54
55 _SockAddrIn.sin_family = family;
56 _SockAddrIn.sin_port = htons (portNo);
57 if (lpctszAddress != NULL)
58 {
59 INT ret = ::InetPton (family, lpctszAddress, &(_SockAddrIn.sin_addr));
60 if (ret != 1)
61 {
62 response = FALSE;
63 }
64 }
65
66 return response;
67}
68
69BOOL SocketBuilder::SetSockOpt (int level, int optname, int value) const
70{
71 BOOL response = TRUE;
72
73 int ret = ::setsockopt (
74 _socket, level, optname, (const char*)&value, sizeof (value));
75 if (ret != 0)
76 {
77 response = FALSE;
78 }
79
80 return response;
81}
82
84{
85 BOOL response = TRUE;
86
87 int ret = ::connect (
88 _socket, (PSOCKADDR)&_SockAddrIn, sizeof (_SockAddrIn));
89 if (ret != 0)
90 {
91 response = FALSE;
92 }
93
94 return response;
95}
96
98{
99 BOOL response = TRUE;
100
101 int ret = ::bind (_socket, (PSOCKADDR)&_SockAddrIn, sizeof (_SockAddrIn));
102 if (ret != 0)
103 {
104 response = FALSE;
105 }
106
107 return response;
108}
109
111{
112 BOOL response = TRUE;
113
114 int ret = ::listen (_socket, 5);
115 if (ret != 0)
116 {
117 response = FALSE;
118 }
119
120 return response;
121}
122
124BOOL SocketBuilder::Ioctl (long cmd, u_long arg) const
125{
126 u_long value = arg;
127 BOOL response = TRUE;
128
129 int ret = ::ioctlsocket (_socket, cmd, &value);
130 if (ret != 0)
131 {
132 response = FALSE;
133 }
134
135 return response;
136}
137
139 LPCTSTR lpctszIpAddr, u_short portNo)
140{
141 UdpConnector* response = NULL;
142 BOOL ret = FALSE;
143
144 do
145 {
146 ret = this->Socket (AF_INET, SOCK_DGRAM, 0);
147 if (ret == FALSE) break;
148
149 ret = this->SetSockAddr (AF_INET, portNo, lpctszIpAddr);
150 if (ret == FALSE) break;
151
152 ret = this->Bind ();
153 if (ret == FALSE) break;
154
155 response = new UdpConnector (_socket);
156 _socket = INVALID_SOCKET;
157 return response;
158
159 } while (FALSE);
160
161 this->Close ();
162
163 return response;
164}
165
166//
167// @sa http://chokuto.ifdef.jp/advanced/function/connect.html
168//
170 LPCTSTR lpctszIpAddr, u_short portNo, int retryInterval, int retryCount)
171{
172 TcpConnector* response = NULL;
173 BOOL ret = FALSE;
174
175 do
176 {
177 ret = this->Socket (AF_INET, SOCK_STREAM, 0);
178 if (ret == FALSE) break;
179
180 // KeepAliveはソフトでの設定はON/OFFだけです。
181 // 実際の設定値はレジストリから取得されます。
182 // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters
183 // KeepAliveTime(ms) 既定値:7,200,000ミリ秒(2時間)
184 // KeepAliveInterval(ms) 既定値:1,000ミリ秒(1秒)
185 ret = this->SetSockOpt (SOL_SOCKET, SO_KEEPALIVE, 1);
186 if (ret == FALSE) break;
187
188 ret = this->SetSockAddr (AF_INET, portNo, lpctszIpAddr);
189 if (ret == FALSE) break;
190
191 // connect()は対向サーバーが別のマシンにある場合、
192 // 21秒のブロッキング時間となります(対向がいない場合など)。
193 // そのため、connect()を呼び出す前に、ソケット関数を非同期
194 // (ノンブロッキング)モードに変更します。
195 ret = this->Ioctl (FIONBIO, 1);
196 if (ret == FALSE) break;
197
198 while (retryCount--)
199 {
200 ret = this->Connect ();
201 if (ret == TRUE || this->GetErrNo () == WSAEISCONN)
202 {
203 // connect()に成功した場合、非同期(ノンブロッキング)
204 // モードを通常のブロッキングモードに戻します。
205 // 失敗した場合は、そのままクローズされます。
206 ret = this->Ioctl (FIONBIO, 0);
207 if (ret == TRUE)
208 {
209 response = new TcpConnector (_socket, lpctszIpAddr, portNo);
210 _socket = INVALID_SOCKET;
211 return response;
212 }
213 }
214 else
215 {
216#ifdef _DEBUG
217 TCHAR tszMessage[80];
218 wsprintf (tszMessage, _T ("[FAILED]connect() error. reason=%d\n"), this->GetErrNo ());
219 OutputDebugString (tszMessage);
220#endif
221 Sleep (retryInterval);
222 }
223 }
224 } while (FALSE);
225
226 this->Close ();
227
228 return response;
229}
230
231BOOL SocketBuilder::Prepare (u_short portNo, LPCTSTR lpctszIpAddr)
232{
233 BOOL response = FALSE;
234
235 do
236 {
237 response = this->Socket (AF_INET, SOCK_STREAM, 0);
238 if (response == FALSE)
239 {
240 break;
241 }
242
243 // ソケットに対してSO_REUSEADDRを設定すると、
244 // TIME_WAIT状態のポートが存在してもbind()が
245 // できるようになります。
246 // @sa also www.geekpage.jp/programming/linux-network/so_reuseaddr.php
247 response = this->SetSockOpt (SOL_SOCKET, SO_REUSEADDR, 1);
248 if (response == FALSE)
249 {
250 break;
251 }
252
253 response = this->SetSockAddr (AF_INET, portNo, lpctszIpAddr);
254 if (response == FALSE)
255 {
256 break;
257 }
258
259 response = this->Bind ();
260 if (response == FALSE)
261 {
262 break;
263 }
264
265 // KeepAliveはソフトでの設定はON/OFFだけです。
266 // 実際の設定値はレジストリから取得されます。
267 // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters
268 // KeepAliveTime(ms) 既定値:7,200,000ミリ秒(2時間)
269 // KeepAliveInterval(ms) 既定値:1,000ミリ秒(1秒)
270 response = this->SetSockOpt (SOL_SOCKET, SO_KEEPALIVE, 1);
271 if (response == FALSE)
272 {
273 break;
274 }
275
276 response = this->Listen ();
277 if (response == FALSE)
278 {
279 break;
280 }
281
282 } while (FALSE);
283
284 return response;
285}
286
288{
289 BOOL ret = FALSE;
290 TcpConnector* response = NULL;
291
292 do
293 {
294 _wsaEvent = ::WSACreateEvent ();
295 if (_wsaEvent == nullptr)
296 {
297 break;
298 }
299
300 SOCKET socketArray[WSA_MAXIMUM_WAIT_EVENTS]{ _socket };
301 WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS]{ _wsaEvent };
302 int arrayIndex = 0;
303
304 int iRet = ::WSAEventSelect (_socket, eventArray[arrayIndex],
305 FD_ACCEPT | FD_CLOSE);
306 if (iRet != 0)
307 {
308 break;
309 }
310
311 DWORD dwEvent = ::WSAWaitForMultipleEvents (++arrayIndex, eventArray,
312 FALSE, WSA_INFINITE, FALSE);
313 if (dwEvent == WSA_WAIT_FAILED)
314 {
315 break;
316 }
317 dwEvent -= WSA_WAIT_EVENT_0;
318
319 WSANETWORKEVENTS events{ 0 };
320 int iEvent = ::WSAEnumNetworkEvents (socketArray[dwEvent],
321 eventArray[dwEvent], &events);
322 if (iEvent != 0)
323 {
324 break;
325 }
326 else
327 {
328 if (events.lNetworkEvents & FD_ACCEPT)
329 {
330 // go ahead.
331 }
332 else
333 {
334 break;
335 }
336 }
337
338 SOCKADDR_IN mySockAddrIn;
339 ZeroMemory (&mySockAddrIn, sizeof (mySockAddrIn));
340 int length = sizeof (mySockAddrIn);
341 SOCKET mySocket = ::accept (socketArray[dwEvent],
342 (PSOCKADDR)&mySockAddrIn, &length);
343 if (mySocket != INVALID_SOCKET)
344 {
345 TCHAR tszAcceptIPAddress[INET_ADDRSTRLEN];
346 PCWSTR ret = ::InetNtop (
347 AF_INET, &mySockAddrIn.sin_addr, tszAcceptIPAddress,
348 INET_ADDRSTRLEN);
349 if (ret != NULL)
350 {
351 USHORT wAcceptPort = ::ntohs (mySockAddrIn.sin_port);
352 response = new TcpConnector (
353 mySocket, tszAcceptIPAddress, wAcceptPort);
354 }
355 }
356 } while (false);
357
358 if (_wsaEvent != WSA_INVALID_EVENT)
359 {
360 ::WSACloseEvent (_wsaEvent);
361 }
362
363 return response;
364}
365
367{
368 return ::WSASetEvent (_wsaEvent);
369}
370
372 LPCTSTR lpctszHostName, LPCTSTR lptszIpAddr) const
373{
374 BOOL response = FALSE;
375 INT ret;
376 ADDRINFO addrInfo;
377 ZeroMemory (&addrInfo, sizeof (addrInfo));
378 addrInfo.ai_family = AF_INET;
379 addrInfo.ai_socktype = SOCK_STREAM;
380 addrInfo.ai_protocol = IPPROTO_TCP;
381 ADDRINFO* result = NULL;
382 DWORD dwBufferLen = 16;
383
384 do
385 {
386 ret = ::GetAddrInfo (
387 lpctszHostName,
388 NULL,
389 (const ADDRINFOW*)&addrInfo,
390 (PADDRINFOW*)&result);
391 if (ret) break;
392
393 ret = ::WSAAddressToString (
394 result->ai_addr,
395 (DWORD)result->ai_addrlen,
396 NULL,
397 (LPWSTR)lptszIpAddr,
398 &dwBufferLen);
399 if (ret) break;
400
401 response = TRUE;
402 } while (false);
403
404 return response;
405}
ソケットに関するWindowsAPIを集約したクラス
プリコンパイル済みヘッダー ファイルです。
int APIENTRY Startup()
Windowsソケットの使用準備
BOOL APIENTRY Prepare(u_short portNo, LPCTSTR lpctszIpAddr=NULL)
TCPポートのlisten(),accept()の準備
BOOL APIENTRY Socket(int af, int type, int protocol)
BOOL CancelWait()
Wait()待機中のキャンセル処理
SOCKADDR_IN _SockAddrIn
Definition: SocketBuilder.h:92
APIENTRY ~SocketBuilder()
デストラクタ
BOOL APIENTRY Ioctl(long cmd, u_long arg) const
TcpConnector *APIENTRY CreateTcpConnector(LPCTSTR lpctszIpAddr, u_short portNo, int retryInterval, int retryCount)
TcpConnectorの作成
UdpConnector *APIENTRY CreateUdpConnector(LPCTSTR lpctszIpAddr, u_short portNo)
UdpConnectorの作成
BOOL APIENTRY Listen() const
BOOL APIENTRY SetSockAddr(ADDRESS_FAMILY family, u_short portNo, LPCTSTR lpctszAddress)
BOOL APIENTRY GetHostByName(LPCTSTR lpctszHostName, LPCTSTR lptszIpAddr) const
ホスト名からIPアドレスのDNS解決
BOOL APIENTRY SetSockOpt(int level, int optname, int value) const
TcpConnector *APIENTRY Wait()
TCP接続のlisten(),accept()待機関数
APIENTRY SocketBuilder()
コンストラクタ
BOOL APIENTRY Bind() const
BOOL APIENTRY Connect() const
DWORD APIENTRY GetErrNo() const
WinSock API呼び出し時にエラーとなった時、 エラーの詳細を返します。
int APIENTRY Close()
ソケットが使用されていた場合、クローズします。
SOCKET _socket
このクラスで管理するソケットオブジェクト
Definition: SocketLibrary.h:63
TCP通信に関するWindowsAPIを集約したクラス
Definition: TcpConnector.h:17
UDP通信に関するWindowsAPIを集約したクラス
Definition: UdpConnector.h:16
Definition: DBLibrary.h:12