forked from huoyu820125/Micro-Development-Kit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSocket.cpp
More file actions
575 lines (522 loc) · 14.3 KB
/
Socket.cpp
File metadata and controls
575 lines (522 loc) · 14.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
// Socket.cpp: implementation of the Socket class.
//
//////////////////////////////////////////////////////////////////////
#include "Socket.h"
#ifdef WIN32
//#include <windows.h>
#pragma comment ( lib, "ws2_32.lib" )
#endif
using namespace std;
namespace mdk
{
Socket::Socket()
{
m_hSocket = INVALID_SOCKET;
m_bBlock = true;
m_bOpened = false;//未打开状态
}
Socket::Socket( SOCKET hSocket, protocol nProtocolType )
{
m_hSocket = hSocket;
m_bBlock = true;
m_bOpened = false;//未打开状态
Init(nProtocolType);
InitWanAddress();
InitLocalAddress();
}
Socket::~Socket()
{
}
/*
功能:Socket初始化(windows专用)
参数:
lpwsaData WSADATA* [In] A pointer to a WSADATA structure. If lpwsaData is not equal to NULL, then the address of the WSADATA structure is filled by the call to ::WSAStartup. This function also ensures that ::WSACleanup is called for you before the application terminates.
返回值:超时返回TRUE,否则返回FALSE
*/
bool Socket::SocketInit(void *lpVoid)
{
#ifdef WIN32
// initialize Winsock library
WSADATA *lpwsaData = (WSADATA *)lpVoid;
WSADATA wsaData;
if (lpwsaData == NULL)
lpwsaData = &wsaData;
WORD wVersionRequested = MAKEWORD(1, 1);
__int32 nResult = WSAStartup(wVersionRequested, lpwsaData);
if (nResult != 0)
return false;
if (LOBYTE(lpwsaData->wVersion) != 1 || HIBYTE(lpwsaData->wVersion) != 1)
{
WSACleanup();
return false;
}
#endif
return true;
}
void Socket::SocketDestory()
{
#ifdef WIN32
WSACleanup();
#endif
}
void Socket::GetLastErrorMsg( string &strError )
{
char strErr[1024];
#ifdef WIN32
LPSTR lpBuffer;
DWORD dwErrorCode = WSAGetLastError();
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwErrorCode,
LANG_NEUTRAL,
(LPTSTR)&lpBuffer,
0,
NULL );
sprintf( strErr, "Socket Error(%ld):%s", dwErrorCode, lpBuffer );
strError = strErr;
LocalFree(lpBuffer);
#else
sprintf( strErr, "socket errno(%d):%s\n", errno, strerror(errno) );
strError = strErr;
#endif
}
bool Socket::InitForIOCP( SOCKET hSocket )
{
#ifdef WIN32
return 0 == setsockopt( hSocket,
SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
(char *)&(hSocket), sizeof(hSocket) );
#else
return true;
#endif
}
//取得套接字句柄
SOCKET Socket::GetSocket()
{
return m_hSocket;
}
void Socket::GetWanAddress( string& strWanIP, int& nWanPort )
{
nWanPort = m_nWanPort;
strWanIP = m_strWanIP;
return;
}
bool Socket::InitWanAddress()
{
if ( INVALID_SOCKET == m_hSocket ) return false;
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
socklen_t nSockAddrLen = sizeof(sockAddr);
if ( SOCKET_ERROR == getpeername( m_hSocket,
(sockaddr*)&sockAddr, &nSockAddrLen ) ) return false;
m_nWanPort = ntohs(sockAddr.sin_port);
m_strWanIP = inet_ntoa(sockAddr.sin_addr);
return true;
}
void Socket::GetLocalAddress( string& strWanIP, int& nWanPort )
{
nWanPort = m_nLocalPort;
strWanIP = m_strLocalIP;
return;
}
bool Socket::InitLocalAddress()
{
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
socklen_t nSockAddrLen = sizeof(sockAddr);
if ( SOCKET_ERROR == getsockname( m_hSocket,
(sockaddr*)&sockAddr, &nSockAddrLen )) return false;
m_nLocalPort = ntohs(sockAddr.sin_port);
m_strLocalIP = inet_ntoa(sockAddr.sin_addr);
return true;
}
/*
功能:一致性初始化,m_hSocket来至于外部创建,还是内部创建
参数:
nProtocolType __int32 [In] A particular protocol to be used with the socket that is specific to the indicated address family.
返回值:成功返回TRUE,失败返回FALSE
*/
bool Socket::Init(protocol nProtocolType)
{
if ( m_bOpened ) return true;
if ( m_hSocket == INVALID_SOCKET )
{
m_hSocket = socket( PF_INET, nProtocolType, 0 );
if ( m_hSocket == INVALID_SOCKET ) return false;
}
m_bOpened = true;
return m_bOpened;
}
/*
功能:客户端函数,发起TCP连接
参数:
lpszHostAddress LPCTSTR [In] 对方Ip地址
nHostPort UINT [In] 对方监听的端口
返回值:成功返回TRUE,失败返回FALSE
*/
bool Socket::Connect( const char *lpszHostAddress, unsigned short nHostPort)
{
if ( NULL == lpszHostAddress ) return false;
sockaddr_in sockAddr;
memset(&sockAddr,0,sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr = inet_addr(lpszHostAddress);
sockAddr.sin_port = htons( nHostPort );
if ( SOCKET_ERROR != connect(m_hSocket, (sockaddr*)&sockAddr, sizeof(sockAddr)) )
{
InitWanAddress();
InitLocalAddress();
return true;
}
return false;
}
//开始网络Service
bool Socket::StartServer( int nPort )
{
if ( !this->Bind( nPort ) ) return false;
return this->Listen();
}
//是关闭状态返回true,否则返回false
bool Socket::IsClosed()
{
return !m_bOpened;
}
/*
功能:断开socket连接
逻辑:支持傻瓜式调用,任何时候都可以调用此函数断开链接,变为断开状态
返回值:永远返回TRUE
*/
void Socket::Close()
{
if ( INVALID_SOCKET == m_hSocket ) return;
if ( m_bOpened )
{
closesocket(m_hSocket);
m_bOpened = false;
}
m_hSocket = INVALID_SOCKET;
return;
}
/*
功能:绑定一个socket句柄,让该类对象在这个句柄上进行操作
逻辑:
傻瓜式绑定,不管类对象之前是否已经绑定了其它套接字,首先会调用傻瓜式函数close关闭连接,然后在绑定新的套接字,
如果没有实现调用Detach解除旧绑定,那么旧的绑定sock将丢失
参数:
hSocket SOCKET [In] 要绑定sock句柄
*/
void Socket::Attach(SOCKET hSocket)
{
m_hSocket = hSocket;
m_bBlock = true;
m_bOpened = true;//未打开状态
InitWanAddress();
InitLocalAddress();
}
/*
功能:解除绑定,返回绑定的socket句柄
返回值:已绑定的socket句柄,可能是一个INVALID_SOCKET,说明之前没有任何绑定
*/
SOCKET Socket::Detach()
{
SOCKET hSocket = m_hSocket;
m_hSocket = INVALID_SOCKET;
m_bBlock = true;
m_bOpened = false;//未打开状态
return hSocket;
}
/*
功能:接收数据
参数:
lpBuf void* [Out] 保存接收的数据
nBufLen __int32 [Out] 收到数据的长度
lSecond long [In] 超时时间秒
lMinSecond long [In] 超时时间毫秒
返回值:实际接收到的字节数,超时返回-1
*/
int Socket::Receive( void* lpBuf, int nBufLen, bool bCheckDataLength, long lSecond, long lMinSecond )
{
if ( TimeOut( lSecond, lMinSecond ) ) return seTimeOut;//超时
int nResult;
int nFlag = 0;
if ( bCheckDataLength ) nFlag = MSG_PEEK;
nResult = recv(m_hSocket, (char*)lpBuf, nBufLen, nFlag);
if ( 0 == nResult ) return seSocketClose;//断开连接
if ( SOCKET_ERROR != nResult ) return nResult;//无异常发生
//socket发生异常
#ifdef WIN32
int nError = GetLastError();
if ( WSAEWOULDBLOCK == nError ) return 0;//非阻塞recv返回,无数据可接收
return seError;
#else
if ( EAGAIN == errno ) return 0;//非阻塞recv返回,无数据可接收
return seError;
#endif
}
/*
功能:调用库函数send发送数据
参数:
lpBuf const void* [In] 发送的数据
nBufLen int [In] 数据长度
nFlags int [In] An indicator specifying the way in which the call is made
返回值:成功实际发送的字节数,失败返回-1
*/
int Socket::Send( const void* lpBuf, int nBufLen, int nFlags )
{
int nSendSize = send(m_hSocket, (char*)lpBuf, nBufLen, nFlags);
if ( 0 > nSendSize )
{
#ifdef WIN32
int nError = GetLastError();
return -1;
#else
return -1;
#endif
}
if ( nSendSize <= nBufLen ) return nSendSize;
return -1;
}
/*
功能:服务端函数,绑定监听的端口与IP
参数:
nSocketPort UINT [In] 监听的端口
lpszSocketAddress LPCTSTR [In] IP
返回值:成功返回TRUE,否则返回FALSE
*/
bool Socket::Bind( unsigned short nPort, char *strIP )
{
memset(&m_sockAddr,0,sizeof(m_sockAddr));
m_sockAddr.sin_family = AF_INET;
if ( NULL == strIP ) m_sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
else
{
unsigned long lResult = inet_addr( strIP );
if ( lResult == INADDR_NONE ) return false;
m_sockAddr.sin_addr.s_addr = lResult;
}
m_sockAddr.sin_port = htons((unsigned short)nPort);
return (SOCKET_ERROR != bind(m_hSocket, (sockaddr*)&m_sockAddr, sizeof(m_sockAddr)));
}
/*
功能:服务端函数,开始监听
参数:
nConnectionBacklog __int32 [In] 最大连接数
返回值:成功返回TRUE,否则返回FALSE
*/
bool Socket::Listen( int nConnectionBacklog )
{
return (SOCKET_ERROR != listen(m_hSocket, nConnectionBacklog));
}
/*
功能:服务端函数,接收客户端连接
参数:
rConnectedSocket Socket [In] client socket对象
返回值:成功返回TRUE,否则返回FALSE
※注:返回TRUE不表示rConnectedSocket等返回参数值有效,因为如果是非阻塞模式,
无连接到达,该函数一样返回TRUE,而此时rConnectedSocket对象句柄将指向INVALID_SOCKET
*/
bool Socket::Accept(Socket& rConnectedSocket)
{
if ( INVALID_SOCKET != rConnectedSocket.m_hSocket ) return false;
socklen_t sockAddLen = 0;
rConnectedSocket.m_hSocket = accept(m_hSocket, NULL, &sockAddLen);
if ( INVALID_SOCKET == rConnectedSocket.m_hSocket )
{
#ifdef WIN32
if ( WSAEWOULDBLOCK == GetLastError() ) return true;//非阻塞返回,无连接请求到达
#else
if ( EAGAIN == errno ) return true;//非阻塞返回,无连接请求到达
#endif
return false;//socket异常
}
rConnectedSocket.m_bOpened = true;
rConnectedSocket.InitWanAddress();
rConnectedSocket.InitLocalAddress();
return true;
}
/*
功能:套接字设置,端口重用等
参数:
nOptionName __int32 [In] The socket option for which the value is to be set
lpOptionValue const void* [In] A pointer to the buffer in which the value for the requested option is supplied
nOptionLen __int32 [In] lpOptionValue的大小
nLevel __int32 [In] The level at which the option is defined; the supported levels include SOL_SOCKET and IPPROTO_TCP. See the Windows Sockets 2 Protocol-Specific Annex (a separate document included with the Platform SDK) for more information on protocol-specific levels
返回值:成功返回TRUE,否则返回FALSE
*/
bool Socket::SetSockOpt(
int nOptionName,
const void* lpOptionValue,
int nOptionLen,
int nLevel)
{
return ( SOCKET_ERROR != setsockopt(
m_hSocket,
nLevel,
nOptionName,
(char *)lpOptionValue,
nOptionLen));
}
/*
功能:超时测试
参数:
lSecond long [In] 超时设置秒
lMinSecond long [In] 超时设置毫秒
返回值:超时返回TRUE,否则返回FALSE
*/
bool Socket::TimeOut( long lSecond, long lMinSecond )
{
if ( lSecond <= 0 && lMinSecond <= 0 ) return false;
//接收超时设置
timeval outtime;//超时结构
outtime.tv_sec = lSecond;
outtime.tv_usec =lMinSecond;
int nSelectRet;
#ifdef WIN32
FD_SET readfds = { 1, m_hSocket };
nSelectRet=::select( 0, &readfds, NULL, NULL, &outtime ); //检查可读状态
#else
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(m_hSocket, &readfds);
nSelectRet=::select(m_hSocket+1, &readfds, NULL, NULL, &outtime); //检查可读状态
#endif
if ( SOCKET_ERROR == nSelectRet )
{
return true;
}
if ( 0 == nSelectRet ) //超时发生,无可读数据
{
return true;
}
return false;
}
//功能:等待数据
bool Socket::WaitData()
{
int nSelectRet;
#ifdef WIN32
FD_SET readfds = { 1, m_hSocket };
nSelectRet=::select( 0, &readfds, NULL, NULL, NULL ); //检查可读状态
#else
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(m_hSocket, &readfds);
nSelectRet=::select(m_hSocket+1, &readfds, NULL, NULL, NULL); //检查可读状态
#endif
if ( SOCKET_ERROR == nSelectRet )
{
return false;
}
if ( 0 == nSelectRet ) //超时发生,无可读数据
{
return false;
}
return true;
}
/*
功能:阻塞方式设置
参数:
bWait bool [In] TRUE阻塞,FALSE非阻塞
返回值:成功返回TRUE,否则返回FALSE
*/
bool Socket::SetSockMode( bool bWait )
{
#ifdef WIN32
m_bBlock = bWait;
unsigned long ul = 1;
if ( m_bBlock ) ul = 0;
else ul = 1;
int ret = ioctlsocket( m_hSocket, FIONBIO, (unsigned long*)&ul );
if ( ret == SOCKET_ERROR )
{
return false;
}
#else
m_bBlock = bWait;
int flags = fcntl( m_hSocket, F_GETFL, 0 ); //取得当前状态设置
if ( !m_bBlock )
fcntl( m_hSocket, F_SETFL, flags|O_NONBLOCK );//追加非阻塞标志
else
fcntl( m_hSocket, F_SETFL, flags&(~O_NONBLOCK&0xffffffff) );//去掉非阻塞标志,阻塞状态
#endif
return true;
}
/*
功能:调用库函数sendto发送UDP数据
参数:
strIP const char* [In] 接收方IP
nPort int [In] 接收方端口
lpBuf const void* [In] 发送的数据
nBufLen int [In] 数据长度
nFlags int [In] An indicator specifying the way in which the call is made
返回值:成功返回实际发送字节数,可能小于请求发送的长度,失败返回常量SOCKET_ERROR,调用WSAGetLastError函数可获取错误信息
*/
int Socket::SendTo( const char *strIP, int nPort, const void* lpBuf, int nBufLen, int nFlags )
{
sockaddr_in sockAddr;
memset(&sockAddr,0,sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(nPort);
sockAddr.sin_addr.s_addr = inet_addr(strIP);
int ret = sendto( m_hSocket, (const char*)lpBuf, nBufLen, nFlags,
(sockaddr*)&sockAddr, sizeof(sockaddr));
if (ret < 0) ret = -1;
return ret;
}
/*
功能:调用库函数recvFrom接收UDP数据
参数:
lpBuf void* [Out] 保存接收的数据
nBufLen int [Out] 收到数据的长度
strFromIP string& [Out] 发送方IP
nFromPort int& [Out] 发送方端口
lSecond long [In] 超时时间秒
lMinSecond long [In] 超时时间毫秒
返回值:实际接收到的字节数,超时返回0
*/
int Socket::ReceiveFrom( char* lpBuf, int nBufLen, string &strFromIP, int &nFromPort, bool bCheckDataLength, long lSecond, long lMinSecond )
{
strFromIP = "";
nFromPort = -1;
if ( 0 >= nBufLen ) return 0;
sockaddr_in sockAddr;
socklen_t nAddrLen = sizeof(sockaddr);
/* waiting for receive data */
int nResult;
int nFlag = 0;
while ( true )
{
if ( TimeOut( lSecond, lMinSecond ) ) return seTimeOut;
if ( bCheckDataLength )nFlag = MSG_PEEK;
nResult = recvfrom(m_hSocket, lpBuf, nBufLen, nFlag, (sockaddr*)&sockAddr, &nAddrLen);
if ( nAddrLen > 0 ) GetAddress(sockAddr, strFromIP, nFromPort);
if ( SOCKET_ERROR == nResult ) //socket发生异常
{
#ifndef WIN32
if ( EAGAIN == errno ) return 0;//非阻塞recv返回,无数据可接收
return seError;
#else
int nError = GetLastError();
if ( 0 == nError )//之前存在失败的udp发送
{
if ( MSG_PEEK == nFlag )//没有删除接收缓冲,从接收缓冲将消息删除
{
recvfrom(m_hSocket, lpBuf, nBufLen, 0, (sockaddr*)&sockAddr, &nAddrLen);
}
continue;
}
if ( WSAEWOULDBLOCK == nError ) return 0;//非阻塞recv返回,无数据可接收
return seError;
#endif
}
break;
}
return nResult;
}
void Socket::GetAddress( const sockaddr_in &sockAddr, string &strIP, int &nPort )
{
nPort = ntohs(sockAddr.sin_port);
strIP = inet_ntoa(sockAddr.sin_addr);
}
}//namespace mdk