VC支持协程已经有一段时间了,之前一直想不明白协程的意义在哪里,前几天拉屎的时候突然灵光一闪:

以下是伪代码:

  1. task server() {
  2. for (;;) {
  3. sock_context s = co_await io.accept();
  4. for (;;) {
  5. auto buf = co_await io.recv(s);
  6. if (!buf.length())
  7. break;
  8.  
  9. std::cout << buf.data() << std::endl;
  10. int n = co_await io.send(s, "收到!", strlen("收到!") + 1);
  11. }
  12. co_await io.close(s);
  13. }
  14. }

如果把IO库对外的接口做成上面这样,那岂不是看起来和最简单的阻塞模型相同的代码结构,但它的内在其实是异步的,用单线程相同的代码就能支撑一堆连接通信。

所以才有了接下来的研究(闲出屁才研究的),好在研究出成品了。

最终我也明白协程的意义了:

  协程化的库越多,C++程序员的门槛会越低,做上层开发的程序员可以不用知道协程的细节,只要知道如何正确使用库即可。

好了,真正介绍协程细节的文章有一大堆,不用我来写,我直接放代码,有兴趣的可以参考我的实现以及那些细节文章自己做:

  1. #pragma once
  2. #include <WinSock2.h>
  3. #include <MSWSock.h>
  4. #include <ws2tcpip.h>
  5. #pragma comment(lib, "ws2_32.lib")
  6. #include <coroutine>
  7. #include <string>
  8. #include <functional>
  9. #include "logger.hpp"
  10.  
  11. /**
  12. * 最近花了点时间学习了一下C++20协程,初步改造实现了IOCP协程化的网络IO库
  13. * 此前基于回调分发的机制,由于上层协议解析所需的各种上下文,导致这个库是模板化的,
  14. * 现在有了协程,上层协议上下文已经可以在协程函数中实现,消除了模板化,也变得易于维护了一丢丢。
  15. * 但目前协程还有多少坑是未知的,是好是坏还得再看。
  16. * 使用协程,就意味着,这个库几乎完全失去了多线程的能力,
  17. * 要维护好一个内部是多线程,外皮是协程的IO库,我承认我没那个脑子。
  18. * 我个人当前的状态是不考虑过度设计,只追求上层代码优雅简洁,10几万并发对我而言已经满足了。
  19. * 如果这还不够用,那就意味着该放弃协程了,协程不是完全没有损耗的,根据我的测试,协程相比回调函数分发的方式,有15%左右的性能损耗。
  20. */
  21. #pragma warning(push)
  22. #pragma warning(disable:4996)
  23. namespace aqx{
  24.  
  25. static int init_winsock() {
  26. WSADATA wd;
  27. return WSAStartup(MAKEWORD(2, 2), &wd);
  28. }
  29.  
  30. static aqx::log nlog;
  31.  
  32. #ifndef _nf
  33. #define _nf ((size_t)-1)
  34. #endif
  35. #ifndef __AQX_TIME_HPP
  36. using clock64_t = long long;
  37. template<typename period = std::milli>
  38. clock64_t now() {
  39. const clock64_t _Freq = _Query_perf_frequency();
  40. const clock64_t _Ctr = _Query_perf_counter();
  41. const clock64_t _Whole = (_Ctr / _Freq) * period::den;
  42. const clock64_t _Part = (_Ctr % _Freq) * period::den / _Freq;
  43. return _Whole + _Part;
  44. }
  45. #endif
  46.  
  47. /**
  48. * 操作码与状态码定义
  49. */
  50. struct net_status {
  51. static constexpr unsigned int s_accept = 0x01;
  52. static constexpr unsigned int s_connect = 0x02;
  53. static constexpr unsigned int s_read = 0x04;
  54. static constexpr unsigned int s_write = 0x08;
  55. static constexpr unsigned int s_close = 0x10;
  56.  
  57. static constexpr unsigned int t_activated = 0x40;
  58.  
  59. static constexpr unsigned int t_acceptor = 0x0100;
  60. static constexpr unsigned int t_connector = 0x0200;
  61. static constexpr unsigned int t_await_undo = 0x0400;
  62.  
  63. static constexpr unsigned int t_await_accept = 0x010000;
  64. static constexpr unsigned int t_await_connect = 0x020000;
  65. static constexpr unsigned int t_await_read = 0x040000;
  66. static constexpr unsigned int t_await_write = 0x080000;
  67. static constexpr unsigned int t_await_close = 0x100000;
  68. static constexpr unsigned int t_await = 0xFF0000;
  69. };
  70.  
  71. /** net_base 主要负责衔接操作系统
  72. * 不考虑过度设计,写得比较辣鸡,能用就行。
  73. */
  74. class net_base {
  75. public:
  76. net_base() {
  77. fd = INVALID_SOCKET;
  78. hIocp = NULL;
  79. AcceptEx = NULL;
  80. ConnectEx = NULL;
  81. DisconnectEx = NULL;
  82. StreamCapacity = 1440;
  83. Timeout = 0;
  84. DataBacklog = 0;
  85. WorkerThreadId = 0;
  86. }
  87.  
  88. static bool sockaddr_from_string(sockaddr_in& _Addr, const std::string& _Dest) {
  89. _Addr.sin_addr.S_un.S_addr = INADDR_NONE;
  90.  
  91. size_t pos = _Dest.find(":");
  92. if(pos == _nf) {
  93. nlog("%s->错误的目标地址:(%s)\n", __FUNCTION__, _Dest.data());
  94. return false;
  95. }
  96.  
  97. auto strip = _Dest.substr(0, pos);
  98. auto strport = _Dest.substr(pos + 1);
  99. strport.erase(strport.find_last_not_of("\r\n\t ") + 1);
  100. strport.erase(0, strport.find_first_not_of("\r\n\t "));
  101. unsigned short port = (unsigned short)atoi(strport.c_str());
  102. if (!port) {
  103. nlog("%s->目标端口号错误:(%s)\n", __FUNCTION__, _Dest.data());
  104. return false;
  105. }
  106.  
  107. strip.erase(strip.find_last_not_of("\r\n\t ") + 1);
  108. strip.erase(0, strip.find_first_not_of("\r\n\t "));
  109. auto it = std::find_if(strip.begin(), strip.end(), [](char c)->bool {
  110. return ((c < '0' || c > '9') && (c != '.'));
  111. });
  112. _Addr.sin_family = AF_INET;
  113. _Addr.sin_port = htons(port);
  114. if (it != strip.end()) {
  115. hostent* host = gethostbyname(strip.c_str());
  116. if (!host) {
  117. nlog("%s->错误的目标域名:(%s)\n", __FUNCTION__, _Dest.data());
  118. return false;
  119. }
  120. _Addr.sin_addr = *(in_addr*)(host->h_addr_list[0]);
  121. }
  122. else {
  123. _Addr.sin_addr.S_un.S_addr = inet_addr(strip.c_str());
  124. }
  125.  
  126. if (_Addr.sin_addr.S_un.S_addr == INADDR_NONE) {
  127. nlog("%s->错误的目标地址:(%s)\n", __FUNCTION__, _Dest.data());
  128. return false;
  129. }
  130. return true;
  131. }
  132.  
  133. static void sockaddr_any(sockaddr_in& _Addr, unsigned short _Port) {
  134. _Addr.sin_family = AF_INET;
  135. _Addr.sin_port = htons(_Port);
  136. _Addr.sin_addr.S_un.S_addr = INADDR_ANY;
  137. }
  138.  
  139. static void sockaddr_local(sockaddr_in& _Addr, unsigned short _Port) {
  140. _Addr.sin_family = AF_INET;
  141. _Addr.sin_port = htons(_Port);
  142. _Addr.sin_addr.S_un.S_addr = INADDR_LOOPBACK;
  143. }
  144.  
  145. static void* getmswsfunc(SOCKET s, GUID guid) {
  146. DWORD dwBytes;
  147. void* lpResult = nullptr;
  148. WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid,
  149. sizeof(guid), &lpResult, sizeof(lpResult), &dwBytes, NULL, NULL);
  150. return lpResult;
  151. }
  152.  
  153. static std::string sockaddr_to_string(const sockaddr_in &_Addr) {
  154. char buf[256];
  155. sprintf(buf, "%d.%d.%d.%d:%d", _Addr.sin_addr.S_un.S_un_b.s_b1,
  156. _Addr.sin_addr.S_un.S_un_b.s_b2,
  157. _Addr.sin_addr.S_un.S_un_b.s_b3,
  158. _Addr.sin_addr.S_un.S_un_b.s_b4,
  159. htons(_Addr.sin_port));
  160. std::string _Result = buf;
  161. return _Result;
  162. }
  163.  
  164. private:
  165. int init(int _StreamCapacity, int _DataBacklog, int _Timeout) {
  166. if (fd != INVALID_SOCKET) {
  167. return 0;
  168. }
  169. auto reterr = [this](int n) {
  170. if (fd != INVALID_SOCKET) {
  171. closesocket(fd);
  172. fd = INVALID_SOCKET;
  173. }
  174. return n;
  175. };
  176. StreamCapacity = _StreamCapacity;
  177. Timeout = _Timeout;
  178. DataBacklog = _DataBacklog;
  179. fd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
  180. if (fd == INVALID_SOCKET) {
  181. nlog("%s->创建套接字失败:%d", __FUNCTION__, WSAGetLastError());
  182. return reterr(-1);
  183. }
  184. ConnectEx = (LPFN_CONNECTEX)getmswsfunc(fd, WSAID_CONNECTEX);
  185. if (!ConnectEx) {
  186. nlog("%s->获取 ConnectEx 地址失败,错误号:%d", __FUNCTION__, WSAGetLastError());
  187. return reterr(-2);
  188. }
  189. AcceptEx = (LPFN_ACCEPTEX)getmswsfunc(fd, WSAID_ACCEPTEX);
  190. if (!AcceptEx) {
  191. nlog("%s->获取 AcceptEx 函数失败,错误号:%d", __FUNCTION__, WSAGetLastError());
  192. return reterr(-3);
  193. }
  194.  
  195. // 我已经不止一次做过DisconnectEx的测试,最终结论都是DisconnectEx并不能提高并发连接数。
  196. // DisconnectEx 在想象中会更快是因为用IOCP队列锁去换系统全局锁带来了性能提升。
  197. // 还有一种方法是开一个线程搞个表去阻塞调用DisconnectEx,完事之后直接AcceptEx,也就最终把全局内核锁完全转嫁成你自己的锁了。
  198. // DisconnectEx首先是不同的操作系统行为不一致,真正保险的做法只能在对方关闭连接时,调用DisconnectEx来复用。
  199. // 对于IOCP来说,也就是在WSARecv或者WSASend 从 GetQueuedCompletionStatus 返回之后,第2个参数transferred == 0时
  200. // 同时它受到TCP TIME_WAIT状态的影响
  201. // 系统存在大量TIME_WAIT套接字时,最终得到的效果是,用了更多内存,去换来了更少的并发连接数。
  202.  
  203. /*DisconnectEx = (LPFN_DISCONNECTEX)getmswsfunc(fd, WSAID_DISCONNECTEX);
  204. if (!DisconnectEx) {
  205. nlog("%s->获取 DisconnectEx 函数失败,错误号:%d", __FUNCTION__, WSAGetLastError());
  206. return reterr(-4);
  207. }*/
  208.  
  209. hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
  210. if (!hIocp) {
  211. nlog("%s->创建完成端口失败,错误号:%d", __FUNCTION__, GetLastError());
  212. return reterr(-5);
  213. }
  214. CreateIoCompletionPort((HANDLE)fd, hIocp, 0, 0);
  215. return 0;
  216. }
  217.  
  218. void close() {
  219. if (fd != INVALID_SOCKET) {
  220. closesocket(fd);
  221. fd = INVALID_SOCKET;
  222. }
  223.  
  224. if (hIocp) {
  225. CloseHandle(hIocp);
  226. hIocp = NULL;
  227. }
  228. }
  229.  
  230. BOOL Accept(SOCKET s, char* _Data, LPOVERLAPPED _Overlapped) {
  231. DWORD _Received = 0;
  232. return AcceptEx(fd, s, _Data, 0, sizeof(SOCKADDR_IN) << 1, sizeof(SOCKADDR_IN) << 1, &_Received, _Overlapped);
  233. }
  234.  
  235. BOOL Connect(SOCKET s, sockaddr* _Addr, int _AddrLen, LPOVERLAPPED _Overlapped) {
  236. DWORD _Sent = 0;
  237. return ConnectEx(s, _Addr, _AddrLen, nullptr, 0, &_Sent, _Overlapped);
  238. }
  239.  
  240. BOOL Disconnect(SOCKET s, LPOVERLAPPED _Overlapped) {
  241. return DisconnectEx(s, _Overlapped, TF_REUSE_SOCKET, 0);
  242. }
  243.  
  244. private:
  245. friend class sock;
  246. friend class netio;
  247. friend class coio;
  248. SOCKET fd;
  249. HANDLE hIocp;
  250. LPFN_ACCEPTEX AcceptEx;
  251. LPFN_CONNECTEX ConnectEx;
  252. LPFN_DISCONNECTEX DisconnectEx;
  253. int StreamCapacity;
  254. int Timeout;
  255. int DataBacklog;
  256. DWORD WorkerThreadId;
  257. };
  258.  
  259. /*直接继承一个std::string来作为套接字的各种缓冲区*/
  260. class sock_buffer : public std::string {
  261. public:
  262. using _Basetype = std::string;
  263. using _Basetype::_Basetype;
  264. void preset_length(size_t _Length) {
  265. // 直接在二进制层面去搞VC的std::string结构,修改std::string::length()的返回值
  266. // 这么做的好处是,免去了std::string::resize()的拷贝问题。
  267. // 注意这段代码仅适用于VC,G++的std::string结构和VC不一样。
  268. struct __stlstr {
  269. const char str[0x10];
  270. size_t len;
  271. };
  272. if (this->capacity() < _Length)
  273. this->reserve(_Length);
  274. ((__stlstr*)this)->len = _Length;
  275. }
  276. };
  277.  
  278. /**
  279. * 协程task
  280. */
  281. template<typename _Ty>
  282. struct net_task_t {
  283. struct promise_type;
  284. using _Hty = std::coroutine_handle<promise_type>;
  285. struct promise_type {
  286. net_task_t get_return_object() { return { _Hty::from_promise(*this) }; }
  287. // initial_suspend 里返回return std::suspend_always{};表示协程初始化成功之后就挂起
  288. // 这里就挂起,是为了给set_sock留出操作的时间,否则一个空函数协程,会在创建完之后直接就销毁。
  289. auto initial_suspend() { return std::suspend_always{}; }
  290.  
  291. auto final_suspend() noexcept {
  292. s->on_destroy_coroutine();
  293. return std::suspend_never{};
  294. }
  295. void unhandled_exception() { std::terminate(); }
  296. void return_void() { }
  297. _Ty* s = nullptr;
  298. };
  299. _Hty _Handle;
  300. void resume() { _Handle.resume(); }
  301. void destroy() { _Handle.destroy(); }
  302. void set_sock(_Ty* _s) { _Handle.promise().s = _s; }
  303. };
  304.  
  305. /**套接字上下文*/
  306. class sock {
  307. // 这是扩展OVERLAPPED结构
  308. struct binding {
  309. OVERLAPPED ol;
  310. int opt;
  311. sock* s;
  312. };
  313.  
  314. /**
  315. * 返回给协程recv的对象类型
  316. */
  317. class sock_data {
  318. sock_data(sock* _s) : s(_s) {}
  319. public:
  320. char* data() { return s->ibuf.data(); }
  321. void erase(size_t _Count) { s->ibuf.erase(0, _Count); }
  322. size_t length() { return s->ibuf.length(); }
  323. void clear() { s->ibuf.clear(); }
  324.  
  325. private:
  326. friend class sock;
  327. sock* s;
  328. };
  329.  
  330. /**返回给协程connect和accept的对象类型
  331. * 用于异步send与close,
  332. * 其他线程也可以利用这个对象通信,已经处理了线程安全问题,但不太效率,因为使用了全局锁。
  333. */
  334. class asyncsock {
  335. public:
  336. /**
  337. * send 是未加锁的发送数据
  338. * 没有多线程需求时,send是安全的
  339. */
  340. int send(void* data, int len) {
  341. return s->send(data, len);
  342. }
  343.  
  344. int send(const void* data, int len) {
  345. return s->send(data, len);
  346. }
  347.  
  348. /**
  349. * 存在多线程异步写需求时,就应该所有的写操作全部用safe_send
  350. *
  351. */
  352. int safe_send(void* data, int len) {
  353. return s->safe_send(data, len);
  354. }
  355.  
  356. int safe_send(const void* data, int len) {
  357. return s->safe_send(data, len);
  358. }
  359.  
  360. void close() {
  361. s->close();
  362. }
  363.  
  364. bool isactivated() { return s->isactivated(); }
  365.  
  366. operator bool() {
  367. return (s != nullptr);
  368. }
  369.  
  370. sockaddr_in& getsockaddr() {
  371. return s->getsockaddr();
  372. }
  373.  
  374. private:
  375. friend class netio;
  376. friend class coio;
  377. friend class sock;
  378. sock* s = nullptr;
  379. };
  380.  
  381. struct recv_awaitable {
  382. recv_awaitable(sock* s) : data(s) { }
  383.  
  384. __declspec(noinline) bool await_ready() {
  385. // 我当前的vs版本是: vs 2022 17.0.1
  386. // 这里发现一个编译bug,只要await_ready与await_suspend同时被inline优化
  387. // 会编译成如下这样的汇编:
  388. /*
  389. * 这段反汇编处于协程函数内部,在删除__declspec(noinline)之后大致就是如下的样子。
  390. 00007FF69D8A60F8 call aqx::coio::recv (07FF69D8A2C70h)
  391.  
  392. // 这里直接用rbx保存了recv_awaitable对象,它是被优化到寄存器的
  393. 00007FF69D8A60FD mov rbx,rax
  394. // 但是在经过IOCP工作线程跳回来之后,发现它并没有维护好rbx寄存器
  395. // 最后导致rbx = __coro_frame_ptr.__resume_address
  396.  
  397. 00007FF69D8A6100 mov rax,qword ptr [rax]
  398. 00007FF69D8A6103 test dword ptr [rax+20h],400h
  399. 00007FF69D8A610A je `main'::`2'::<lambda_1>$_ResumeCoro$1::operator()+448h (07FF69D8A6318h)
  400. 00007FF69D8A6110 lea rcx,[rax+0B8h]
  401. 00007FF69D8A6117 cmp qword ptr [rax+0D0h],10h
  402. 00007FF69D8A611F jb `main'::`2'::<lambda_1>$_ResumeCoro$1::operator()+258h (07FF69D8A6128h)
  403. 00007FF69D8A6121 mov rcx,qword ptr [rax+0B8h]
  404. 00007FF69D8A6128 mov qword ptr [rax+0C8h],r14
  405. 00007FF69D8A612F mov byte ptr [rcx],0
  406. 00007FF69D8A6132 mov rax,qword ptr [rbx]
  407. 00007FF69D8A6135 and dword ptr [rax+20h],0FFFFFBFFh
  408. 00007FF69D8A613C mov r8,qword ptr [rbx]
  409. 00007FF69D8A613F mov rdx,rbx
  410. */
  411. // 我目前尚未完全搞清楚导致这个问题的机制(还没有真正闲出屁去帮微软找bug)。
  412. // 所以我没上报这个问题。
  413. // 有兴趣的同学可以仔细跟踪一下。
  414. // 我目前的缓解措施是,让await_ready强制noinline,也总比在await_suspend里面resume()强。
  415.  
  416. if (data.s->st & net_status::t_await_undo) {
  417. data.s->ibuf.clear();
  418. data.s->st &= (~net_status::t_await_undo);
  419. return true;
  420. }
  421. return false;
  422. }
  423.  
  424. void await_suspend(std::coroutine_handle<> handle) { }
  425. sock_data await_resume() const {
  426. return data;
  427. }
  428. sock_data data;
  429. };
  430.  
  431. struct sock_awaitable {
  432. sock_awaitable(sock* _s) { s.s = _s; }
  433. __declspec(noinline) bool await_ready() {
  434. if (s.s->st & net_status::t_await_undo) {
  435. s.s->st &= (~net_status::t_await_undo);
  436. return true;
  437. }
  438. return false;
  439. }
  440. void await_suspend(std::coroutine_handle<> handle) { }
  441. sock::asyncsock await_resume() { return s; }
  442. sock::asyncsock s;
  443. };
  444.  
  445. struct close_awaitable {
  446. close_awaitable(bool _IsSuspend) : IsSuspend(_IsSuspend) { }
  447. __declspec(noinline) bool await_ready() { return (IsSuspend == false); }
  448. void await_suspend(std::coroutine_handle<> handle) { }
  449. void await_resume() { }
  450. bool IsSuspend;
  451. };
  452.  
  453. struct send_awaitable {
  454. send_awaitable(sock* _s) : s(_s) {}
  455. __declspec(noinline) bool await_ready() {
  456. if (s->st & net_status::t_await_undo) {
  457. s->st &= (~net_status::t_await_undo);
  458. return true;
  459. }
  460. return false;
  461. }
  462. void await_suspend(std::coroutine_handle<> handle) { }
  463. int await_resume() { return s->syncsendlen; }
  464. sock* s;
  465. };
  466.  
  467. struct safe_buffer {
  468. sock* s = nullptr;
  469. sock_buffer buf;
  470. };
  471.  
  472. public:
  473. using opcode = net_status;
  474. sock(net_base* _v) {
  475. fd = INVALID_SOCKET;
  476. v = _v;
  477. st = 0;
  478. memset(&input.ol, 0, sizeof(input.ol));
  479. memset(&output.ol, 0, sizeof(output.ol));
  480.  
  481. if (v->Timeout)
  482. output.ol.hEvent = input.ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  483. else
  484. output.ol.hEvent = input.ol.hEvent = NULL;
  485. output.s = input.s = this;
  486. output.opt = opcode::s_write;
  487. ibuf.reserve(v->StreamCapacity);
  488. obuf.reserve(v->StreamCapacity);
  489. }
  490.  
  491. ~sock() {
  492. close();
  493. if (!output.ol.hEvent)
  494. return;
  495. CloseHandle(output.ol.hEvent);
  496. output.ol.hEvent = output.ol.hEvent = NULL;
  497. if (st & opcode::t_await)
  498. co.destroy();
  499. }
  500.  
  501. void on_destroy_coroutine() {
  502. st &= (~opcode::t_connector);
  503. }
  504.  
  505. bool isactivated() {
  506. return ((st & opcode::t_activated) != 0);
  507. }
  508.  
  509. int send(void* data, int len) {
  510. if (!len)
  511. return len;
  512. int n = (int)(obuf.capacity() - obuf.length());
  513. if (n >= len) {
  514. obuf.append((char*)data, len);
  515. }
  516. else {
  517. if (v->DataBacklog != 0 && obacklog.length() + len > v->DataBacklog) {
  518. //积压值超过限制
  519. close();
  520. return -1;
  521. }
  522. obacklog.append((char*)data, len);
  523. }
  524. return (write() == 0) ? len : -1;
  525. }
  526.  
  527. int send(const void* data, int len) {
  528. return send((void*)data, len);
  529. }
  530.  
  531. int safe_send(void* data, int len) {
  532. std::lock_guard<std::mutex> lg(mtx);
  533. return send(data, len);
  534. }
  535.  
  536. int safe_send(const void* data, int len) {
  537. std::lock_guard<std::mutex> lg(mtx);
  538. return send(data, len);
  539. }
  540.  
  541. void close() {
  542. if (INVALID_SOCKET == fd)
  543. return;
  544. closesocket(fd);
  545. fd = INVALID_SOCKET;
  546. st &= ~opcode::t_activated;
  547. st |= opcode::s_close;
  548. set_timer(false);
  549. ibuf.clear();
  550. if (obacklog.capacity() <= 0x0F)
  551. return;
  552. sock_buffer tmp;
  553. obacklog.swap(tmp);
  554. }
  555.  
  556. sockaddr_in& getsockaddr() { return sa; }
  557.  
  558. private:
  559. int initfd() {
  560. if (INVALID_SOCKET != fd) {
  561.  
  562. return 0;
  563. }
  564.  
  565. fd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
  566. if (INVALID_SOCKET == fd) {
  567. nlog("%s->创建套接字失败,错误号:%d", __FUNCTION__, WSAGetLastError());
  568. return -1;
  569. }
  570. LINGER linger = { 1, 0 };
  571. setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
  572. int b = 1;
  573. setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&b, sizeof(b));
  574. CreateIoCompletionPort((HANDLE)fd, v->hIocp, 0, 0);
  575. return 0;
  576. }
  577.  
  578. int bindlocal() {
  579. sockaddr_in local;
  580. local.sin_family = AF_INET;
  581. local.sin_addr.S_un.S_addr = INADDR_ANY;
  582. local.sin_port = 0;
  583. if (SOCKET_ERROR == bind(fd, (LPSOCKADDR)&local, sizeof(local))) {
  584. nlog("%s->绑定本地端口失败,错误号:%d", __FUNCTION__, WSAGetLastError());
  585. return -1;
  586. }
  587. return 0;
  588. }
  589.  
  590. bool set_dest(const std::string& _Dest) {
  591. return net_base::sockaddr_from_string(sa, _Dest);
  592. }
  593.  
  594. void set_timer(bool _Enable) {
  595. if (_Enable) {
  596. if (hTimer)
  597. return;
  598. RegisterWaitForSingleObject(&hTimer, output.ol.hEvent, [](void* Param, BOOLEAN TimerOrWaitFired) {
  599. if (!TimerOrWaitFired)
  600. return;
  601. sock* p = (sock*)Param;
  602. PostQueuedCompletionStatus(p->v->hIocp, 0, (ULONG_PTR)p, nullptr);
  603. }, this, (ULONG)v->Timeout, WT_EXECUTEDEFAULT);
  604. }
  605. else {
  606. if (!hTimer)
  607. return;
  608. std::ignore = UnregisterWaitEx(hTimer, NULL);
  609. hTimer = NULL;
  610. }
  611. }
  612.  
  613. int nat() {
  614. sockaddr_in _Addr;
  615. int _AddrLen = sizeof(_Addr);
  616. if (-1 == getsockname(fd, (sockaddr*)&_Addr, &_AddrLen))
  617. return -1;
  618. SOCKET fdNat = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
  619. LINGER linger = { 1, 0 };
  620. setsockopt(fdNat, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
  621. CreateIoCompletionPort((HANDLE)fdNat, v->hIocp, 0, 0);
  622. if (-1 == bind(fdNat, (sockaddr*)&_Addr, sizeof(_Addr))) {
  623. closesocket(fdNat);
  624. return -1;
  625. }
  626. close();
  627. fd = fdNat;
  628. return connect();
  629. }
  630.  
  631. int accept() {
  632. if (((st & 0xFF) | opcode::s_close) != opcode::s_close) {
  633. nlog("%s->当前套接字未断开连接!", __FUNCTION__);
  634. return -1;
  635. }
  636.  
  637. if (initfd())
  638. return -1;
  639. DWORD _Received = 0;
  640. input.opt = opcode::s_accept;
  641. st &= (~opcode::s_close);
  642. st |= opcode::s_accept;
  643. if (!v->Accept(fd, ibuf.data(), &input.ol)) {
  644. int _Error = WSAGetLastError();
  645. if (_Error != ERROR_IO_PENDING) {
  646. st &= (~opcode::s_accept);
  647. nlog("%s->AcceptEx失败, 错误号:", __FUNCTION__, WSAGetLastError());
  648. return -1;
  649. }
  650. }
  651. return 0;
  652. }
  653.  
  654. int connect() {
  655. if (((st & 0xFF) | opcode::s_close) != opcode::s_close) {
  656. nlog("%s->当前套接字未断开连接!", __FUNCTION__);
  657. return -1;
  658. }
  659. if (INVALID_SOCKET == fd) {
  660. if (initfd())
  661. return -1;
  662. if (bindlocal())
  663. return -1;
  664. }
  665. input.opt = opcode::s_connect;
  666. st &= (~opcode::s_close);
  667. st |= opcode::s_connect;
  668.  
  669. if (!v->Connect(fd, (sockaddr*)&sa, sizeof(sa), &input.ol)) {
  670. int _Error = WSAGetLastError();
  671. if (_Error != ERROR_IO_PENDING) {
  672. nlog("%s->ConnectEx失败, 错误号:", __FUNCTION__, WSAGetLastError());
  673. return -1;
  674. }
  675. }
  676. return 0;
  677. }
  678.  
  679. int write() {
  680. if (!(st & opcode::t_activated)) {
  681. return -1;
  682. }
  683. if (st & (opcode::s_write | opcode::s_close | opcode::s_accept | opcode::s_connect))
  684. return 0;
  685. if (obacklog.size()) {
  686. size_t rl = obuf.capacity() - obuf.length();
  687. if (rl > obacklog.length())
  688. rl = obacklog.length();
  689. if (rl) {
  690. obuf.append(obacklog.data(), rl);
  691. obacklog.erase(0, rl);
  692. }
  693. }
  694. WSABUF buf = { (ULONG)(obuf.length()), obuf.data() };
  695. if (!buf.len)
  696. return 0;
  697. st |= opcode::s_write;
  698. DWORD _Sent = 0;
  699. if (SOCKET_ERROR == WSASend(fd, &buf, 1, &_Sent, 0, &(output.ol), NULL)) {
  700. int _Error = WSAGetLastError();
  701. if (WSA_IO_PENDING != _Error) {
  702. st &= (~opcode::s_write);
  703. return -1;
  704. }
  705. }
  706. return 0;
  707. }
  708.  
  709. int read() {
  710. if (!(st & opcode::t_activated)) {
  711. return -1;
  712. }
  713. if (st & (opcode::s_read | opcode::s_close | opcode::s_accept | opcode::s_connect))
  714. return 0;
  715. WSABUF buf = {
  716. (ULONG)(ibuf.capacity() - ibuf.length()),
  717. ibuf.data() + ibuf.length()
  718. };
  719. if ((int)buf.len <= 0) {
  720. return -1;
  721. }
  722. DWORD _Received = 0;
  723. DWORD _Flags = 0;
  724. st |= opcode::s_read;
  725. input.opt = opcode::s_read;
  726. if (SOCKET_ERROR == WSARecv(fd, &buf, 1, &_Received, &_Flags, &(input.ol), NULL)) {
  727. int _Error = WSAGetLastError();
  728. if (WSA_IO_PENDING != _Error) {
  729. st &= ~(opcode::s_read);
  730. return -1;
  731. }
  732. }
  733. return 0;
  734. }
  735.  
  736. private:
  737. friend class coio;
  738. friend class netio;
  739. SOCKET fd;
  740. sockaddr_in sa;
  741. net_base* v;
  742. int st;
  743. binding input, output, reuse_sock;
  744. sock_buffer ibuf, obuf, obacklog;
  745. HANDLE hTimer;
  746. aqx::clock64_t rtime;
  747. net_task_t<sock> co;
  748. int syncsendlen;
  749. std::mutex mtx;
  750. };
  751.  
  752. // coio是传参给协程函数的操作对象
  753. class coio {
  754. coio(sock* _s) : s(_s) {}
  755.  
  756. public:
  757. using asyncsock = sock::asyncsock;
  758. using sock_awaitable = sock::sock_awaitable;
  759. using close_awaitable = sock::close_awaitable;
  760. using send_awaitable = sock::send_awaitable;
  761. using recv_awaitable = sock::recv_awaitable;
  762.  
  763. struct nat_awaitable {
  764. nat_awaitable(bool _ret) : ret(_ret) { }
  765. __declspec(noinline) bool await_ready() { return (ret == false); }
  766. void await_suspend(std::coroutine_handle<> handle) { }
  767. bool await_resume() { return ret; }
  768. bool ret;
  769. };
  770.  
  771. coio() : s(nullptr) {}
  772.  
  773. sock_awaitable connect(const std::string& _Dest) {
  774. if (!s->set_dest(_Dest)) {
  775. // 设置目标地址失败时,撤销等待。
  776. s->st |= net_status::t_await_undo;
  777. return sock_awaitable(s);
  778. }
  779.  
  780. // 我使用的协程initial_suspend中是不挂起的,
  781. // 所以一个套接字的首次connect操作基本都是由其他线程引发的
  782. // 而且很可能在await_suspend之前,IOCP队列就已经完成
  783. if (GetCurrentThreadId() == s->v->WorkerThreadId) {
  784. if (s->connect()) {
  785. // 连接失败时,撤销等待。
  786. s->st |= net_status::t_await_undo;
  787. return sock_awaitable(s);
  788. }
  789. }
  790. else {
  791. // 因此,不是IOCP队列线程引发的connect就发送到IOCP队列去处理
  792. PostQueuedCompletionStatus(s->v->hIocp, net_status::s_connect, (ULONG_PTR)s, 0);
  793. }
  794.  
  795. s->st |= net_status::t_await_connect;
  796. return sock_awaitable(s);
  797. }
  798.  
  799. sock_awaitable accept() {
  800. // 首次accept虽然也是其他线程调用的(一般是main线程)
  801. // 但首次accept时,IOCP工作线程尚未启动,因此可以无视掉connect的那个问题。
  802. s->st |= ((!s->accept()) ? net_status::t_await_accept : net_status::t_await_undo);
  803. return sock_awaitable(s);
  804. }
  805.  
  806. /**
  807. * 以下几个成员函数中的参数asyncsock _s应该等同于私有成员s,除非强行在外部使用syncio对象
  808. * 使用参数而不是私有成员的原因是防止在尚未连接前调用IO操作。
  809. * 私有成员s将专用于accept与connect
  810. */
  811. close_awaitable close(asyncsock _s) {
  812. _s.s->close();
  813. if ((_s.s->st & 0xFF) == net_status::s_close) {
  814. // 如果套接字上已经没有任何IO事件,就让awaitable直接唤醒协程
  815. // 通常这才是正常状态,但如果有其他线程异步send时,可能就会有未决IO存在了。
  816. return close_awaitable(false);
  817. }
  818. _s.s->st |= net_status::t_await_close;
  819. return close_awaitable(true);
  820. }
  821.  
  822. send_awaitable send(asyncsock _s, void *buf, int len) {
  823. _s.s->syncsendlen = _s.send(buf, len);
  824. _s.s->st |= ((_s.s->syncsendlen >= 0) ? net_status::t_await_write : net_status::t_await_undo);
  825. return sock::send_awaitable(_s.s);
  826. }
  827.  
  828. send_awaitable send(asyncsock _s, const void* buf, int len) {
  829. _s.s->syncsendlen = _s.send(buf, len);
  830. _s.s->st |= ((_s.s->syncsendlen >= 0) ? net_status::t_await_write : net_status::t_await_undo);
  831. return sock::send_awaitable(_s.s);
  832. }
  833.  
  834. send_awaitable safe_send(asyncsock _s, void* buf, int len) {
  835. _s.s->syncsendlen = _s.safe_send(buf, len);
  836. _s.s->st |= ((_s.s->syncsendlen >= 0) ? net_status::t_await_write : net_status::t_await_undo);
  837. return sock::send_awaitable(_s.s);
  838. }
  839.  
  840. send_awaitable safe_send(asyncsock _s, const void* buf, int len) {
  841. _s.s->syncsendlen = _s.safe_send(buf, len);
  842. _s.s->st |= ((_s.s->syncsendlen >= 0) ? net_status::t_await_write : net_status::t_await_undo);
  843. return sock::send_awaitable(_s.s);
  844. }
  845.  
  846. recv_awaitable recv(asyncsock _s) {
  847. int n = _s.s->read();
  848. if (n < 0) {
  849. _s.s->st |= net_status::t_await_undo;
  850. }
  851. else {
  852. _s.s->st |= net_status::t_await_read;
  853. }
  854. return recv_awaitable(_s.s);
  855. }
  856.  
  857. nat_awaitable nat(asyncsock _s, const std::string& _Dest) {
  858. if ((_s.s->st & 0xFF) != net_status::t_activated) {
  859. // nat之前必须保证所有未决IO都已经返回,与打洞服务器保持正常连接状态,否则就是失败。
  860. // 到这里失败时,依旧与打洞服务器保持着正常连接。
  861. return nat_awaitable(false);
  862. }
  863.  
  864. sockaddr_in sa = _s.s->sa;
  865. if (!_s.s->set_dest(_Dest)) {
  866. // 设置目标地址失败
  867. // 到这里失败时,依旧与打洞服务器保持着正常连接。
  868. _s.s->sa = sa;
  869. return nat_awaitable(false);
  870. }
  871.  
  872. if (_s.s->nat()) {
  873. // 到这一步失败时,与打洞服务器的连接就有可能会断掉
  874. // nat失败时,本就应该直接close();
  875. // 都失败了,我想不出还要跟打洞服务器继续苟合的理由。
  876. // 如果所有状态全都对,还失败,可能就是双方正好属于无法穿透的NAT类型环境下。
  877. // 我对此研究不多,业界内真正懂行的也不多,资料更是少得可怜,我只知道TCP NAT在代码上的表现为:
  878. // 1、与打洞服务器保持连接的这个套接字设置了SO_REUSEADDR,确保这个套接字绑定的本地端口可复用。
  879. // 在这个库里我全都设置了可复用,但主要目的是为了缓解TIME_WAIT,并不是为了穿透。
  880. // 2、双方通过打洞服务器沟通好各自的远端地址
  881. // 3、双方都创建一个新的套接字,并将该套接字绑定到本地与打洞服务器进行连接的那个地址(getsockname可以获得)
  882. // 到第 3 步处理好之后,与打洞服务器连接的那个套接字,已经废了,无法再进行通信,此时应该把它close掉。
  883. // 4、最后双方都connect对方的地址。
  884. _s.s->sa = sa;
  885. return nat_awaitable(false);
  886. }
  887.  
  888. s->st |= net_status::t_await_connect;
  889. return nat_awaitable(true);
  890. }
  891.  
  892. bool valid() {
  893. return (s != nullptr);
  894. }
  895.  
  896. operator bool () {
  897. return valid();
  898. }
  899.  
  900. private:
  901. friend class netio;
  902. sock* s;
  903. };
  904.  
  905. /**
  906. * 可以简单把netio看成是一个容器的作用
  907. * 它主要用于对接net_base,创建线程,处理IO事件。
  908. */
  909. class netio {
  910. struct IOCP_STATUS {
  911. DWORD transferred;
  912. SIZE_T key;
  913. typename sock::binding* pb;
  914. BOOL ok;
  915. };
  916.  
  917. public:
  918. /**listener 只是一种简单的参数包装,只是为了方便构造而已
  919. * 构造参数:
  920. * _Dest 要监听的地址和端口,格式为:"a.b.c.d:port"
  921. * _ListenBacklog 系统函数listen的第2个参数
  922. * _MaxClients 最多同时接受的客户端数量
  923. */
  924. class listener {
  925. public:
  926. listener() {
  927. max_clients = 0;
  928. listen_backlog = 0;
  929. addr.sin_addr.S_un.S_addr = INADDR_NONE;
  930. }
  931.  
  932. listener(const std::string& _Dest, int _ListenBacklog, size_t _MaxClients) {
  933. max_clients = _MaxClients;
  934. listen_backlog = _ListenBacklog;
  935. net_base::sockaddr_from_string(addr, _Dest);
  936. }
  937.  
  938. private:
  939. friend class netio;
  940. sockaddr_in addr;
  941. int listen_backlog;
  942. size_t max_clients;
  943. };
  944.  
  945. using asyncsock = sock::asyncsock;
  946. using opcode = net_status;
  947. using task = net_task_t<sock>;
  948.  
  949. int init(int _StreamCapacity = 1440, int _DataBacklog = 0, int _Timeout = 0) {
  950. std::lock_guard<std::mutex> lg(mtx);
  951. return nwb.init(_StreamCapacity, _DataBacklog, _Timeout);
  952. }
  953.  
  954. int server(const std::function<task(coio)> &_func, const listener &param) {
  955. std::lock_guard<std::mutex> lg(mtx);
  956. if (thd.joinable()) {
  957. nlog("%s->netio已启动, 请勿重复调用!", __FUNCTION__);
  958. return 0;
  959. }
  960.  
  961. if (nwb.fd == INVALID_SOCKET)
  962. return -1;
  963.  
  964. cofunc = _func;
  965. if (param.addr.sin_addr.S_un.S_addr != INADDR_NONE) {
  966. if (SOCKET_ERROR == bind(nwb.fd, (SOCKADDR*)&param.addr, sizeof(SOCKADDR))) {
  967. nlog("%s->绑定端口失败,错误号:%d", __FUNCTION__, WSAGetLastError());
  968. nwb.close();
  969. return -1;
  970. }
  971.  
  972. if (SOCKET_ERROR == ::listen(nwb.fd, param.listen_backlog)) {
  973. nlog("%s->监听失败,错误号:%d", __FUNCTION__, WSAGetLastError());
  974. nwb.close();
  975. return -1;
  976. }
  977.  
  978. for (int i = 0; i < param.max_clients; i++) {
  979. sock* psock = new sock(&nwb);
  980. a_list.push_back(psock);
  981. psock->st |= opcode::t_acceptor;
  982. psock->co = cofunc(coio(psock));
  983. psock->co.set_sock(psock);
  984. psock->co.resume();
  985. }
  986. }
  987. __start();
  988. return 0;
  989. }
  990.  
  991. // client是一次性的,专用于客户端
  992. // 让它返回asyncsock对象的理由是为了给脚本语言预留的
  993. // 例如可以使用lua去实现类似node.js的那种connect之后不管连没连上就先得到对象去绑定事件的机制。
  994. asyncsock client(const std::function<task(coio)>& _func) {
  995. std::lock_guard<std::mutex> lg(mtx);
  996. coio io;
  997. asyncsock ret;
  998. if (!thd.joinable()) {
  999. // 如果线程未启动,尝试启动线程,这之后如果要回收资源,是需要stop和release的
  1000. if (nwb.fd == INVALID_SOCKET)
  1001. return ret;
  1002. __start();
  1003. }
  1004. io.s = get_connector();
  1005. ret.s = io.s;
  1006. io.s->co = _func(io);
  1007. io.s->co.set_sock(io.s);
  1008. io.s->co.resume();
  1009. return ret;
  1010. }
  1011.  
  1012. void stop() {
  1013. std::lock_guard<std::mutex> lg(mtx);
  1014. if (thd.joinable()) {
  1015. PostQueuedCompletionStatus(nwb.hIocp, -1, 0, 0);
  1016. thd.join();
  1017. }
  1018. }
  1019.  
  1020. void release() {
  1021. std::lock_guard<std::mutex> lg(mtx);
  1022. if (thd.joinable()) {
  1023. nlog("%s->nio正在运行,请先stop", __FUNCTION__);
  1024. return;
  1025. }
  1026. for (auto p : a_list) {
  1027. if (p->st & opcode::t_await)
  1028. p->co.destroy();
  1029. delete p;
  1030. }
  1031. a_list.clear();
  1032.  
  1033. for (auto p : c_list) {
  1034. if (p->st & opcode::t_await)
  1035. p->co.destroy();
  1036. delete p;
  1037. }
  1038. c_list.clear();
  1039. nwb.close();
  1040. }
  1041.  
  1042. private:
  1043. sock* get_connector() {
  1044. sock* psock = nullptr;
  1045.  
  1046. for (auto v : c_list) {
  1047. if ((v->st & opcode::t_connector) == 0 && ((v->st & 0xFF)| opcode::s_close) == opcode::s_close) {
  1048. psock = v;
  1049. break;
  1050. }
  1051. }
  1052.  
  1053. if (!psock) {
  1054. psock = new sock(&nwb);
  1055. c_list.push_back(psock);
  1056. }
  1057.  
  1058. psock->st |= opcode::t_connector;
  1059. return psock;
  1060. }
  1061.  
  1062. void on_connect(sock& s) {
  1063. s.ibuf.clear();
  1064. s.obuf.clear();
  1065. s.obacklog.clear();
  1066. s.rtime = aqx::now() + nwb.Timeout;
  1067. if (nwb.Timeout != 0)
  1068. s.set_timer(true);
  1069. s.st |= opcode::t_activated;
  1070. }
  1071.  
  1072. void on_accept(sock &s) {
  1073. // 懒得去调用GetAcceptExSockAddrs,有硬编码可用
  1074. #ifndef _WIN64
  1075. s.sa = *(sockaddr_in*)(s.ibuf.data() + 0x26);
  1076. #else
  1077. s.sa = *(sockaddr_in*)(s.ibuf.data() + 0x20);
  1078. #endif
  1079. on_connect(s);
  1080. }
  1081.  
  1082. bool on_resume(sock& s) {
  1083. if (s.st & opcode::t_await) {
  1084. // 清除所有协程等待标志
  1085. s.st &= (~opcode::t_await);
  1086.  
  1087. // 唤醒协程
  1088. s.co.resume();
  1089. return true;
  1090. }
  1091. return false;
  1092. }
  1093.  
  1094. void on_close(sock& s) {
  1095. if ((s.st & 0xFF) == opcode::s_close) {
  1096. s.st &= ~opcode::s_close;
  1097. on_resume(s);
  1098. }
  1099. }
  1100.  
  1101. bool error_resume(sock &s) {
  1102. int st = s.st & opcode::t_await;
  1103. switch (st) {
  1104. case opcode::t_await_accept:
  1105. case opcode::t_await_connect:
  1106. case opcode::t_await_close:
  1107. s.st &= (~opcode::t_await);
  1108. s.co.resume();
  1109. return true;
  1110. case opcode::t_await_read:
  1111. s.ibuf.clear();
  1112. s.st &= (~opcode::t_await);
  1113. s.co.resume();
  1114. return true;
  1115. case opcode::t_await_write:
  1116. s.syncsendlen = -1;
  1117. s.st &= (~opcode::t_await);
  1118. s.co.resume();
  1119. return true;
  1120. default:
  1121. break;
  1122. }
  1123. return false;
  1124. }
  1125.  
  1126. void on_reset(sock &s) {
  1127. if ((s.st & 0xFF) == opcode::s_close) {
  1128. s.st &= ~opcode::s_close;
  1129. if (s.st & opcode::t_acceptor) {
  1130. // 如果服务端协程不在一个循环里,协程返回自动销毁后就会这样
  1131. // 此时的挽救措施就是创建一个新的协程
  1132. s.co = cofunc(coio(&s));
  1133. }
  1134. }
  1135. }
  1136.  
  1137. void on_completion(IOCP_STATUS& st) {
  1138. sock& s = *(st.pb->s);
  1139. int op = st.pb->opt;
  1140. s.st &= (~op);
  1141. if (s.st & opcode::s_close)
  1142. op = 0;
  1143. //nlog("on_completion:%I64X, %d", &s, op);
  1144. switch (op) {
  1145. case 0:
  1146. break;
  1147. case opcode::s_accept:
  1148. on_accept(s);
  1149. break;
  1150. case opcode::s_connect:
  1151. if (!st.ok && WSAGetLastError() == 1225) {
  1152. // 出现这种错误,一般是由于服务端没有在监听指定端口,直接被操作系统拒绝了。
  1153. op = 0;
  1154. break;
  1155. }
  1156. on_connect(s);
  1157. break;
  1158. case opcode::s_read:
  1159. if (!st.transferred) {
  1160. op = 0;
  1161. break;
  1162. }
  1163. s.ibuf.preset_length(s.ibuf.length() + st.transferred);
  1164. break;
  1165. case opcode::s_write:
  1166. if (!st.transferred) {
  1167. op = 0;
  1168. break;
  1169. }
  1170.  
  1171. s.obuf.erase(0, st.transferred);
  1172. if (s.obuf.length() || s.obacklog.length()) {
  1173. if (s.write()) {
  1174. op = 0;
  1175. break;
  1176. }
  1177. }
  1178. // write操作可能是非协程发起的,协程很可能挂起在recv,因此需要判断一下。
  1179. if (!(s.st & opcode::t_await_write))
  1180. return;
  1181. break;
  1182. }
  1183.  
  1184. //nlog("on_completion2:%I64X, %d", &s, op);
  1185. if (!op) {
  1186. if (error_resume(s))
  1187. return;
  1188. // 只有当协程被销毁时,error_resume才会返回false
  1189. s.close();
  1190. on_reset(s);
  1191. return;
  1192. }
  1193.  
  1194. on_resume(s);
  1195. if (s.st & opcode::s_close)
  1196. return on_close(s);
  1197. }
  1198.  
  1199. void __start() {
  1200. thd = std::thread([this]() {
  1201. nwb.WorkerThreadId = GetCurrentThreadId();
  1202. srand((unsigned int)aqx::now() + nwb.WorkerThreadId);
  1203. IOCP_STATUS st = { 0,0,0,0 };
  1204. //nlog("netio::worker->I/O工作线程 %d 开始!", nwb.WorkerThreadId);
  1205. for (;;) {
  1206. st.ok = GetQueuedCompletionStatus(nwb.hIocp,
  1207. &(st.transferred),
  1208. &(st.key),
  1209. (OVERLAPPED**)&(st.pb),
  1210. INFINITE);
  1211.  
  1212. if (!st.pb) {
  1213. if (!st.transferred) {
  1214. sock* psock = (sock*)st.key;
  1215. if (aqx::now() > psock->rtime && (psock->st & opcode::t_activated)) {
  1216. psock->close();
  1217. if (error_resume(*psock))
  1218. return;
  1219. on_reset(*psock);
  1220. }
  1221. }
  1222. else if (st.transferred == opcode::s_connect) {
  1223. sock* psock = (sock*)st.key;
  1224. if (psock->connect()) {
  1225. psock->close();
  1226. if (error_resume(*psock))
  1227. return;
  1228. on_reset(*psock);
  1229. }
  1230. }
  1231. else if (st.transferred == -1)
  1232. break;
  1233. continue;
  1234. }
  1235. on_completion(st);
  1236. }
  1237.  
  1238. //nlog("netio::worker->I/O工作线程 %d 已停止!", nwb.WorkerThreadId);
  1239. });
  1240. }
  1241.  
  1242. private:
  1243. net_base nwb;
  1244. std::list<sock*> a_list;
  1245. std::list<sock*> c_list;
  1246. std::function<task(coio)> cofunc;
  1247. std::thread thd;
  1248. std::mutex mtx;
  1249. };
  1250. }
  1251.  
  1252. #pragma warning(pop)

这个库我已经去除了各种耦合,除了日志库,aqx::log我自己写的一个简单的格式化日志库:

logger.hpp
#pragma once
#include <iostream>
#include <string>
#include <time.h>
#include <stdarg.h>
#include <mutex>
#include <vector> //aqx::log不与aqx其他库耦合
#if defined(_WIN32) || defined(_WIN64)
#ifndef _WINDOWS_
#include <WinSock2.h>
#endif
#define __aqxlog_getpid GetCurrentProcessId
#define __aqxlog_gettid GetCurrentThreadId
#include <io.h>
#else
#if defined(__linux__)
#include <unistd.h>
#include <sys/syscall.h>
#define __aqxlog_getpid getpid
#define __aqxlog_gettid() syscall(__NR_gettid)
#endif
#endif #pragma warning(push)
#pragma warning(disable:4996) namespace aqx { class log {
private:
struct _format_texts {
std::string time;
std::string type;
std::string pid;
std::string tid;
}; public:
static constexpr auto hs_time{ static_cast<int>(1) };
static constexpr auto hs_type{ static_cast<int>(2) };
static constexpr auto hs_pid{ static_cast<int>(4) };
static constexpr auto hs_tid{ static_cast<int>(8) }; log() {
_stdout_fp = stdout;
fp = stdout;
_fmtts = { "%Y/%m/%d %H:%M:%S ", "{%s} ", "[%d] ", "(%d) " };
head_style = log::hs_time;
head_presize = _gethps();
_EnableInfo = true;
_EnableError = false;
_EnableDebug = false;
_EnableWarn = false;
_DefType = "info";
s.reserve(0x1000);
} ~log() {
if (fp != _stdout_fp)
fclose(fp);
} void enable(const std::string_view& _Type, bool _Enable) {
std::lock_guard<std::mutex> lg(_Mtx);
if (_Type == "info")
_EnableInfo = _Enable;
else if (_Type == "error")
_EnableError = _Enable;
else if (_Type == "debug")
_EnableDebug = _Enable;
else if (_Type == "warn")
_EnableWarn = _Enable;
} void seths(int hs) {
std::lock_guard<std::mutex> lg(_Mtx);
head_style = hs;
head_presize = _gethps();
} void sethfmt(int _Style, const char* _Fmt) {
std::lock_guard<std::mutex> lg(_Mtx);
switch (_Style) {
case hs_time:
_fmtts.time = _Fmt;
break;
case hs_type:
_fmtts.type = _Fmt;
break;
case hs_pid:
_fmtts.pid = _Fmt;
break;
case hs_tid:
_fmtts.tid = _Fmt;
break;
}
head_presize = _gethps();
} bool setvfs(const char* _FileName, bool _PutStdout = false) {
std::lock_guard<std::mutex> lg(_Mtx);
FILE* _tmp = fopen(_FileName, "ab");
if (!_tmp)
return false;
if (fp != _stdout_fp)
fclose(fp);
fp = _tmp;
PutStdout = _PutStdout;
return true;
} log& info(const char* _Fmt, ...) {
std::lock_guard<std::mutex> lg(_Mtx);
if (!_EnableInfo)
return *this;
va_list vl;
va_start(vl, _Fmt);
_build("info", _Fmt, vl);
va_end(vl);
_putlog();
return *this;
} log& debug(const char* _Fmt, ...) {
std::lock_guard<std::mutex> lg(_Mtx);
if (!_EnableDebug)
return *this;
va_list vl;
va_start(vl, _Fmt);
_build("info", _Fmt, vl);
va_end(vl);
_putlog();
return *this;
} log& error(const char* _Fmt, ...) {
std::lock_guard<std::mutex> lg(_Mtx);
if (!_EnableError)
return *this;
va_list vl;
va_start(vl, _Fmt);
_build("info", _Fmt, vl);
va_end(vl);
_putlog();
return *this;
} log& warn(const char* _Fmt, ...) {
std::lock_guard<std::mutex> lg(_Mtx);
if (!_EnableWarn)
return *this;
va_list vl;
va_start(vl, _Fmt);
_build("info", _Fmt, vl);
va_end(vl);
_putlog();
return *this;
} log& operator()(const char* _Fmt, ...) {
std::lock_guard<std::mutex> lg(_Mtx);
if (!_EnableInfo)
return *this;
va_list vl;
va_start(vl, _Fmt);
_build(_DefType.c_str(), _Fmt, vl);
va_end(vl);
_putlog();
return *this;
} private:
void _putlog() {
fputs(s.data(), fp);
if (fp != _stdout_fp) {
//fflush(fp);
if (PutStdout)
fputs(s.data(), _stdout_fp);
}
} size_t _build(const char* _Type, const char* _Fmt, va_list vl) {
s.clear();
size_t n = vsnprintf(nullptr, 0, _Fmt, vl);
if (n <= 0)
return _build_head(_Type);
if (n >= s.capacity()) {
s.clear();
s.reserve(n + head_presize);
}
size_t _Pos = _build_head(_Type);
char* p = (char*)s.data();
_Pos += vsnprintf(p + _Pos, s.capacity(), _Fmt, vl);
char c = p[_Pos - 1];
#ifdef _WINDOWS_
if (c != '\r' && c != '\n') {
p[_Pos++] = '\r';
p[_Pos++] = '\n';
p[_Pos] = '\0';
} #else
if (c != '\r' && c != '\n') {
p[_Pos++] = '\n';
p[_Pos] = '\0';
}
#endif return _Pos;
} size_t _build_time(size_t _Pos) {
if (!(head_style & log::hs_time))
return _Pos;
time_t t = time(NULL);
auto _Tm = localtime(&t);
_Pos += strftime((char*)s.data() + _Pos, head_presize, _fmtts.time.c_str(), _Tm);
return _Pos;
} size_t _build_type(size_t _Pos, const char* _Type) {
if (!(head_style & log::hs_type))
return _Pos;
_Pos += sprintf((char*)s.data() + _Pos, _fmtts.type.c_str(), _Type);
return _Pos;
} size_t _build_pid(size_t _Pos) {
if (!(head_style & log::hs_pid))
return _Pos;
auto _Pid = __aqxlog_getpid();
_Pos += sprintf((char*)s.data() + _Pos, _fmtts.pid.c_str(), _Pid);
return _Pos;
} size_t _build_tid(size_t _Pos) {
if (!(head_style & log::hs_tid))
return _Pos;
auto _Tid = __aqxlog_gettid();
_Pos += sprintf((char*)s.data() + _Pos, _fmtts.tid.c_str(), _Tid);
return _Pos;
} size_t _build_head(const char* _Type) {
return _build_tid(_build_pid(_build_type(_build_time(0), _Type)));
} size_t _gethps() {
size_t _Result = 3;
if (head_style & log::hs_time)
_Result += ((_fmtts.time.length() << 1) + 30);
if (head_style & log::hs_type)
_Result += ((_fmtts.pid.length() << 1) + 12);
if (head_style & log::hs_pid)
_Result += ((_fmtts.pid.length() << 1) + 20);
if (head_style & log::hs_tid)
_Result += ((_fmtts.pid.length() << 1) + 20);
return _Result;
} private:
std::vector<char> s;
FILE* fp;
_format_texts _fmtts;
int head_style;
size_t head_presize;
bool PutStdout;
FILE* _stdout_fp;
std::mutex _Mtx;
std::string _DefType;
bool _EnableInfo;
bool _EnableDebug;
bool _EnableError;
bool _EnableWarn;
};
} static aqx::log logger;
#pragma warning(pop)

最后是测试代码:客户端和服务端放在一起了,要分离就从nio.init后面的几个地方分离一下。

// main.cpp
#include <iostream>
#include <aqx/netio.hpp> int main()
{
aqx::init_winsock(); aqx::netio nio;
nio.init(1440, 0x10000); // 一个简单的echo服务器例子: nio.server([](aqx::coio io)->aqx::netio::task {
// 服务端始终应该放在一个死循环里,否则兜底逻辑会反复创建新协程。
for (;;) {
// io.accept会返回一个可用于异步send和close的对象
auto s = co_await io.accept();
logger("客户端连入:%s", aqx::net_base::sockaddr_to_string(s.getsockaddr()));
for (;;) {
auto buf = co_await io.recv(s);
if (!buf.length()) {
logger("断开连接!");
break;
} puts(buf.data());
buf.clear();
// 异步发送,协程不会在这里挂起
s.send("收到!", 5); }
co_await io.close(s);
logger("已关闭!");
}
}, aqx::netio::listener("0.0.0.0:55554", 100, 100)); // 我已经懒到让客户端和服务端都放在一起了,要分自己分
auto sock1 = nio.client([](aqx::coio io)->aqx::netio::task {
// 客户端只有需要自动重连,才放在循环里处理
for (;;) {
auto s = co_await io.connect("127.0.0.1:55554");
if (!s) {
co_await io.close(s);
continue;
} for (;;) {
auto buf = co_await io.recv(s);
if (!buf.length()) {
break;
}
puts(buf.data());
buf.clear();
} co_await io.close(s);
} }); // 我已经懒到让客户端和服务端都放在一起了,要分自己分
auto sock2 = nio.client([](aqx::coio io)->aqx::netio::task {
// 客户端只有需要自动重连,才放在循环里处理
for (;;) {
auto s = co_await io.connect("127.0.0.1:55554");
if (!s) {
co_await io.close(s);
continue;
} for (;;) {
auto buf = co_await io.recv(s);
if (!buf.length()) {
break;
}
puts(buf.data());
buf.clear();
} co_await io.close(s);
} }); std::string str;
for (;;) {
std::cin >> str;
if (str == "exit")
break; std::string sd = "sock1:";
sd += str;
sock1.safe_send(sd.data(), (int)sd.length() + 1); sd = "sock2:";
sd += str;
sock2.safe_send(sd.data(), (int)sd.length() + 1);
} nio.stop();
nio.release();
}

C++20协程实例:携程化的IOCP服务端/客户端的更多相关文章

  1. PHP-Socket服务端客户端发送接收通信实例详解

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://fighter.blog.51cto.com/1318618/1533957 So ...

  2. 【gRPC】C++异步服务端客户端API实例及代码解析

    对于同步API而言,程序的吞吐量并不高.因为在每次发送一个gRPC请求时,会阻塞整个线程,必须等待服务端的ack回到客户端才能继续运行或者发送下一个请求,因此异步API是提升程序吞吐量的必要手段. g ...

  3. [精华][推荐]CAS SSO单点登录服务端客户端实例

    1.修改server.xml文件,如下: 注意: 这里使用的是https的认证方式,需要将这个配置放开,并做如下修改: <Connector port="8443" prot ...

  4. WebService 服务端客户端 实例 HTTPRIO (一) SOAP WSDL

    Delphi中WebService包含的组件解释(有7个)     (1) THTTPRIO-------:使用Http消息来调用远程使用SOAP的接口对象     (2) THTTPReqResp- ...

  5. Android native进程间通信实例-socket本地通信篇之——服务端进程异常退出解决办法

    导读: 好难受啊,为什么服务端说挂就挂,明明只是客户端关闭而已,服务端怎么能挂呢? 想想,如果手机上使用一个聊天程序的时候,手机端关闭了聊天程序,那么远端服务器程序总不能说挂就挂吧!所以一定要查明真相 ...

  6. Netty 的基本简单实例【服务端-客户端通信】

    Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象. 在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理. Accept连接和读写 ...

  7. 2014 CodingTrip - 携程编程大赛 (预赛第二场)

    1001: 食物链(poj1182),直接贴代码,稍作可过 并查集 // // main.cpp // 携程1 // // Created by zhang on 14-4-11. // Copyri ...

  8. python 携程asyncio实现高并发示例1

    import asyncio #携程(携程不是函数) async def print_hello(): while True: print("hello world") await ...

  9. ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存

    分享 最近在公司成功落地了一个用ASP.NET Core 开发前台的CMS项目,虽然对于表层的开发是兼容MVC5的,但是作为爱好者当然要用尽量多的ASP.NET Core新功能了. 背景 在项目开发的 ...

随机推荐

  1. Huffman算法

    一.Huffman算法介绍 霍夫曼编码(英语:Huffman Coding),又译为哈夫曼编码.赫夫曼编码,是一种用于无损数据压缩的熵编码(权编码)算法.在计算机数据处理中,霍夫曼编码使用变长编码表对 ...

  2. 它说你的代码有 Bug「GitHub 热点速览 v.21.44」

    作者:HelloGitHub-小鱼干 本周热点上的榜单大多数提升工作效率的实用工具,像是一个 API 管理所有通知消息(包括推送.邮件-)的 notifire,再是高速解析 JSON 文件的 simd ...

  3. JS控制文本框禁止输入特殊字符

    JS 控制不能输入特殊字符<input type="text" class="domain" onkeyup="this.value=this. ...

  4. 开发笔记-----Ajax 基础使用

    一.GET 方式的用法: 1 <!--html --> 2 <div class="layui-form"> 3 <div class="l ...

  5. 干货分享之spring框架源码分析02-(对象创建or生命周期)

    记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新.欢迎大家指正! 环境: spring5.X + idea 之前分析了Spring读取xml文件的所有信息封装成beanDef ...

  6. Vue 之 Mixins (混入)的使用

    是什么 混入 (mixins): 是一种分发 Vue 组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被合并到组件本身,也就是说父组件调用混入 ...

  7. 🏆【Alibaba中间件技术系列】「RocketMQ技术专题」帮你梳理RocketMQ或Kafka的选择理由以及二者PK

    前提背景 大家都知道,市面上有许多开源的MQ,例如,RocketMQ.Kafka.RabbitMQ等等,现在Pulsar也开始发光,今天我们谈谈笔者最常用的RocketMQ和Kafka,想必大家早就知 ...

  8. 9组-Ahlpa-6/3

    一.基本情况 队名:不行就摆了吧 组长博客:https://www.cnblogs.com/Microsoft-hc/p/15546622.html 小组人数: 8 二.冲刺概况汇报 卢浩玮 过去两天 ...

  9. 美妙绝伦面向node引用-zico图标(逐浪矢量全真图标)1.9发布

    15年前,那个农村小伙初入广告行业被讥笑没有审美 于是他狠下决心,积极研发,缔就技术之核, 再后来,那些PPT和美工er们随便怎么自好,无法让其心怵. 因为他是中华人民共和国唯一具备web.cms.o ...

  10. 3、使用ListOperations操作redis(List列表)

    文章来源:https://www.cnblogs.com/shiguotao-com/p/10560354.html 方法 c参数 s说明   List<V> range(K key, l ...