题记

windows平台下对于服务器高并发的网络模型选型中,使用libevent是个不错的选择。 
本文的背景基于:国内博客对于libevent大多介绍linux实现,大多是c语言的实现,Windows平台下C++相对较少或者较旧。 
本文针对libevent从服务端、客户端两个角度实现了封装类,真正做到,我们在使用封装类时候,只需要关注业务逻辑的处理,不需要关注socket的通信的细节。

1. Libevent介绍

[维基百科]libevent是一个异步事件处理软件函式库,以BSD许可证发布。libevent提供了一组应用程序编程接口(API),让程序员可以设定某些事件发生时所执行的回调函数,也就是说,libevent可以用来取代网络服务器所使用的事件循环检查框架。由于可以省去对网络的处理,且拥有不错的效能,有些软件使用libevent作为网络底层的函式库,如:memcached、Tor。

[libevent官网介绍] 
libevent API提供了一种机制,用于在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持基于信号或常规超时的回调。 
libevent旨在替换在事件驱动的网络服务器中的事件循环机制。应用程序只需要调用event_dispatch()接口,然后动态添加或删除事件,而不必更改事件循环。 
目前,libevent支持 /dev/poll, kqueue, event ports, POSIX select, Windows select(), poll 和 epoll。 其内在的事件机制是完全独立于外在的事件API ,并且libevent的简单更新可以提供新的功能,而无需重新设计应用程序。因此: 
1)Libevent允许便携式应用程序开发,并提供操作系统上可用的最可扩展的事件通知机制。 
2)Libevent也可以用于多线程应用程序,通过隔离每个event_base,以便只有一个线程访问它,或通过锁定访问单个共享的event_base。 Libevent可以在Linux,* BSD,Mac OS X,Solaris,Windows等上编译。 
3)Libevent还为缓冲网络IO提供了复杂的框架,支持套接字,过滤器,速率限制,SSL,零拷贝文件传输和IOCP。 
4)Libevent包括对几个有用的协议的支持,包括DNS,HTTP和最小的RPC框架。

以下关于阻塞、非阻塞,同步、异步,大牛陈硕的经典回复。 
在处理 IO 的时候,阻塞和非阻塞都是同步 IO。 
只有使用了特殊的 API 才是异步 IO。 

2.为什么要使用封装好的网络库?

[陈硕]网络编程是什么?是熟练使用Sockets API吗?说实话,在实际项目里我只用过两次Sockets API,其他时候都是使用封装好的网络库。 
使用封装好的网络库如libevent, muduo网络库 目的之一就是想让日常的网络编程从Sockets API的琐碎细节中解脱出来,让程序员专注于业务逻辑,把时间用在刀刃上。 程序员的主要工作是在事件处理函数中实现业务逻辑,而不是和Sockets API较劲。 
陈硕认为网络编程也可以分为三个层次: 
1). 读过教程和文档 
2). 熟悉本系统TCP/IP协议栈的脾气 
3). 自己写过一个简单的TCP/IP stack

陈硕认为TCP网络编程有三个例子最值得学习研究,分别是echo、chat、proxy,都是长连接协议。 
Echo的作用:熟悉服务端被动接受新连接、收发数据、被动处理连接断开。每个连接是独立服务的,连接之间没有关联。在消息内容方面Echo有一些变种:比如做成一问一答的方式,收到的请求和发送响应的内容不一样,这时候要考虑打包与拆包格式的设计,进一步还可以写简单的HTTP服务。

3.Libevent通信核心

服务端核心步骤简化如下:

步骤1:设置sockfd为nonblocking;

步骤2:使用bufferevent_socket_new创建一个struct bufferevent *bev,关联该sockfd,托管给event_base;

步骤3:使用bufferevent_setcb(bev, read_cb, write_cb, error_cb, (void *)arg)将EV_READ/EV_WRITE对应的函数;

步骤4:使用bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST)来启动read/write事件;

其中,在read_cb里面从input读取数据,处理完毕后塞到output中,会自动写入到sockfd; 
在write_cb里面(对于一个echo server来说,read_cb足够了) 
在error_cb里面处理遇到的错误。使用bufferevent_set_timeout(bev, struct timeval *READ, struct timeval *WRITE)来设置读写超时,在error_cb里面也可以处理超时。 
可以使用bev中libevent的API提取出event_base,sockfd,input/output等相关数据。 
客户端的操作步骤详见代码,提炼即可。

4.C++封装的libevent Echo类

分为:服务端YuLibeventServer类和客户端YuLibeventClient类。

//服务端核心代码如下参考地址:

  1. #include "YuLibeventServer.h"
  2. /*
  3. **@author: laoyang360
  4. **@date: 20161211
  5. **@brief: The server of SimLibeventClient
  6. */
  7. static int s_iBlockSize = 10;
  8. #define MAX_LINE 1024
  9. YuLibeventServer *YuLibeventServer::pThis = NULL;
  10. YuLibeventServer::YuLibeventServer()
  11. {
  12. pThis = this; //将this指针赋给pThis,使得回调函数能通过pThis指针访问本对象
  13. m_pBase = NULL;
  14. m_pListener = NULL;
  15. m_pEvstop = NULL;
  16. }
  17. YuLibeventServer::~YuLibeventServer()
  18. {
  19. }
  20. /*
  21. **@author: laoyang360
  22. **@date: 20161211
  23. **@param: evutil_socket_t fd
  24. **@brief: 设置非阻塞,禁止Nagle算法。
  25. */
  26. void YuLibeventServer::set_tcp_no_delay(evutil_socket_t fd)
  27. {
  28. int iOne = 1;
  29. setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&iOne, sizeof iOne);
  30. }
  31. /*
  32. **@author: laoyang360
  33. **@date: 20161211
  34. **@param: evutil_socket_t fd
  35. **@brief: 等待接受客户端连接处理accept、一个新客户端连接上服务器了
  36. */
  37. void YuLibeventServer::accept_conn_cb(evconnlistener *listener, evutil_socket_t fd,
  38. struct sockaddr *sock, int socklen, void *arg)
  39. {
  40. printf("We got a new connection! Set up a bufferevent for it. accept a client %d\n", fd);
  41. event_base *base = evconnlistener_get_base(listener);
  42. //为这个客户端分配一个bufferevent
  43. bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
  44. set_tcp_no_delay(fd);
  45. bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);
  46. bufferevent_enable(bev, EV_READ | EV_WRITE);
  47. }
  48. /*
  49. **@author: laoyang360
  50. **@date: 20161211
  51. **@param: bufferevent *bev, void *arg
  52. **@brief: echo_read_cb回调接口
  53. */
  54. void YuLibeventServer::echo_read_cb(bufferevent *bev, void *arg)
  55. {
  56. char msg[MAX_LINE + 1] = {0};
  57. int iLen = 0;
  58. evutil_socket_t fd = bufferevent_getfd(bev);
  59. while (iLen = bufferevent_read(bev, msg, sizeof(msg)-1 ), iLen > 0)
  60. {
  61. msg[iLen] = '\0';
  62. printf("fd=%u, read len = %d\t read msg: %s\n", fd, iLen, msg);
  63. int iRst = bufferevent_write(bev, msg, iLen);
  64. if (-1 == iRst)
  65. {
  66. printf("[socket_write_cb]:error occur!\n");
  67. }
  68. }
  69. /*
  70. char reply[] = "[server: i'm server, send 1111]";
  71. printf("writecb: len = %d\n", 1 + strlen(reply));
  72. int iRst = bufferevent_write(bev, reply, 1 + strlen(reply));
  73. if (-1 == iRst)
  74. {
  75. printf("[socket_write_cb]:error occur!\n");
  76. }
  77. */
  78. /*This callback is invoked when there is data to read on bev */
  79. //struct evbuffer *input = bufferevent_get_input(bev);
  80. //struct evbuffer *output = bufferevent_get_output(bev);
  81. /*把input buffer中的所有数据 拷贝到 output buffer*/
  82. //evbuffer_add_buffer(output, input);
  83. }
  84. /*
  85. **@author: laoyang360
  86. **@date: 20161211
  87. **@param: bufferevent *bev, void *arg
  88. **@brief: socket_write_cb回调接口,暂时未使用
  89. */
  90. void YuLibeventServer::socket_write_cb(bufferevent *bev, void *arg)
  91. {
  92. /*
  93. char reply[] = "[server: i'm server, send 1111]";
  94. printf("writecb: len = %d\n", 1 + strlen(reply));
  95. int iRst = bufferevent_write(bev, reply, 1 + strlen(reply));
  96. if (-1 == iRst)
  97. {
  98. printf("[socket_write_cb]:error occur!\n");
  99. }
  100. */
  101. }
  102. /*
  103. **@author: laoyang360
  104. **@date: 20161211
  105. **@param: bufferevent *bev, short events, void *arg
  106. **@brief: echo_event_cb事件处理或异常处理
  107. */
  108. void YuLibeventServer::echo_event_cb(bufferevent *bev, short events, void *arg)
  109. {
  110. struct evbuffer *output = bufferevent_get_output(bev);
  111. size_t remain = evbuffer_get_length(output);
  112. if (events & BEV_EVENT_TIMEOUT)
  113. {
  114. printf("Timed out\n"); //if bufferevent_set_timeouts() called.
  115. }
  116. else if (events & BEV_EVENT_EOF)
  117. {
  118. printf("connection closed, remain %d\n", remain);
  119. }
  120. else if (events & BEV_EVENT_ERROR)
  121. {
  122. printf("some other error, remain %d\n", remain);
  123. }
  124. //这将自动close套接字和free读写缓冲区
  125. bufferevent_free(bev);
  126. }
  127. /*
  128. **@author: laoyang360
  129. **@date: 20161211
  130. **@param: bufferevent *bev, short events, void *arg
  131. **@brief: signal_cb停止信号处理
  132. */
  133. void YuLibeventServer::signal_cb(evutil_socket_t sig, short events, void *arg)
  134. {
  135. struct event_base *base = (event_base *)arg;
  136. printf("exception: interrupt, stop now!\n");
  137. event_base_loopexit(base, NULL);
  138. }
  139. /*
  140. **@author: laoyang360
  141. **@date: 20161211
  142. **@param: int port, 传入端口。
  143. **@brief: libevent,socket初始化等
  144. */
  145. void YuLibeventServer::init(int port)
  146. {
  147. WSADATA wsaData;
  148. DWORD Ret;
  149. if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
  150. {
  151. printf("WSAStartup failed with error %d\n", Ret);
  152. exit(1);
  153. }
  154. m_pBase = event_base_new();
  155. if (NULL == m_pBase)
  156. {
  157. printf("couldn't open event base!\n");
  158. exit(1);
  159. }
  160. m_pEvstop = evsignal_new(m_pBase, SIGINT, signal_cb, m_pBase);
  161. evsignal_add(m_pEvstop, NULL);
  162. struct sockaddr_in sin;
  163. memset(&sin, 0, sizeof(struct sockaddr_in));
  164. sin.sin_family = AF_INET;
  165. sin.sin_port = htons(port);
  166. m_pListener = evconnlistener_new_bind(m_pBase, accept_conn_cb, NULL,
  167. LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,
  168. -1, (struct sockaddr*)&sin,
  169. sizeof(struct sockaddr_in));
  170. if (NULL == m_pListener)
  171. {
  172. printf("couldn't create listener!\n");
  173. exit(1);
  174. }
  175. }
  176. /*
  177. **@author: laoyang360
  178. **@date: 20161211
  179. **@param: 无
  180. **@brief: 启动,循环执行
  181. */
  182. void YuLibeventServer::start()
  183. {
  184. event_base_dispatch(m_pBase);
  185. }
  186. /*
  187. **@author: laoyang360
  188. **@date: 20161211
  189. **@param: 无
  190. **@brief: 停止
  191. */
  192. void YuLibeventServer::stop()
  193. {
  194. if (NULL != m_pListener)
  195. {
  196. evconnlistener_free(m_pListener);
  197. }
  198. if (NULL != m_pEvstop)
  199. {
  200. event_free(m_pEvstop);
  201. }
  202. if (NULL != m_pBase)
  203. {
  204. event_base_free(m_pBase);
  205. }
  206. }

//客户端核心代码参考地址:

  1. #include "yuLibEventClient.h"
  2. /*
  3. **@author: laoyang360
  4. **@date: 20161211
  5. **@brief: The client of SimLibeventClient
  6. */
  7. YuLibeventClient *YuLibeventClient::pThis = NULL;
  8. const static char* s_serverIpAddr = "127.0.0.1";
  9. const static int s_iBlockSize = 10;
  10. const static long s_iTimeOut = 10; //超时时间
  11. const static int s_iSessionCnt = 10;
  12. int YuLibeventClient::m_siLtotal_bytes_read = 0;
  13. int YuLibeventClient::m_siLtotal_messages_read = 0;
  14. YuLibeventClient::YuLibeventClient()
  15. {
  16. pThis = this; //将this指针赋给pThis,使得回调函数能通过pThis指针访问本对象
  17. m_pBase = NULL;
  18. m_pListener = NULL;
  19. m_pszMsg = NULL;
  20. m_evtimeout = NULL;
  21. m_bevs = NULL;
  22. }
  23. YuLibeventClient::~YuLibeventClient()
  24. {
  25. }
  26. /*
  27. **@author: laoyang360
  28. **@date: 20161211
  29. **@param: evutil_socket_t fd
  30. **@brief: 设置非阻塞,禁止Nagle算法。
  31. */
  32. void YuLibeventClient::set_tcp_no_delay(evutil_socket_t fd)
  33. {
  34. int iOne = 1;
  35. setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&iOne, sizeof iOne);
  36. }
  37. /*
  38. **@author: laoyang360
  39. **@date: 20161211
  40. **@param: evutil_socket_t fd, short events, void *arg
  41. **@brief: 超时回调函数。
  42. */
  43. void YuLibeventClient::timeoutcb(evutil_socket_t fd, short events, void *arg)
  44. {
  45. struct event_base *base = (event_base*)arg;
  46. printf("timeout...\n");
  47. event_base_loopexit(base, NULL);
  48. }
  49. /*
  50. **@author: laoyang360
  51. **@date: 20161211
  52. **@param: int fd, short events, void* arg
  53. **@brief: 暂时未使用
  54. */
  55. void YuLibeventClient::cmd_msg_cb(int fd, short events, void* arg)
  56. {
  57. printf("server_msg_cb ing....\n");
  58. struct bufferevent* bev = (struct bufferevent*)arg;
  59. char msg[1024] = "testlaoyang20161210";
  60. int iLen = 1 + strlen(msg);
  61. /*int iLen = bufferevent_read(bev, msg, sizeof(msg));
  62. if (0 == iLen)
  63. {
  64. printf("recv message empty.\n");
  65. exit(1);
  66. }*/
  67. //把终端的消息发送给服务器端
  68. bufferevent_write(bev, msg, iLen);
  69. }
  70. /*
  71. **@author: laoyang360
  72. **@date: 20161211
  73. **@param: struct bufferevent* bev, void* arg
  74. **@brief: writecb回调函数,暂时未使用
  75. */
  76. void YuLibeventClient::writecb(struct bufferevent* bev, void* arg)
  77. {
  78. /*
  79. printf("send_server_cb running....\n");
  80. char szSendMsg[1024] = "[writecb: i'am client]";
  81. int iLen = 1 + strlen(szSendMsg);
  82. printf("iLen = %d\n", iLen);
  83. //把终端的消息发送给服务器端
  84. bufferevent_write(bev, szSendMsg, iLen);
  85. */
  86. }
  87. /*
  88. **@author: laoyang360
  89. **@date: 20161211
  90. **@param: struct bufferevent* bev, void* arg
  91. **@brief: readcb回调函数,接收处理回调接口。
  92. */
  93. void YuLibeventClient::readcb(struct bufferevent* bev, void* arg)
  94. {
  95. char szRecvMsg[1024] = {0};
  96. int len = bufferevent_read(bev, szRecvMsg, sizeof(szRecvMsg));
  97. szRecvMsg[len] = '\0';
  98. printf("recv from server: cnt = %d, len = %d, msg = %s\n", m_siLtotal_messages_read, len, szRecvMsg);
  99. ++m_siLtotal_messages_read;
  100. m_siLtotal_bytes_read += len;
  101. //把终端的消息发送给服务器端
  102. bufferevent_write(bev, szRecvMsg, len);
  103. //以下是chenshuo的使用方法
  104. /*This callback is invoked when there is data to read on bev @by chenshuo below */
  105. //struct evbuffer *input = bufferevent_get_input(bev);
  106. //struct evbuffer *output = bufferevent_get_output(bev);
  107. //++m_siLtotal_messages_read;
  108. //m_siLtotal_bytes_read += evbuffer_get_length(input);
  109. //evbuffer_add_buffer(output, input);
  110. }
  111. /*
  112. **@author: laoyang360
  113. **@date: 20161211
  114. **@param: struct bufferevent *bev, short event, void *arg
  115. **@brief: eventcb回调函数,事件或出错处理回调接口。
  116. */
  117. void YuLibeventClient::eventcb(struct bufferevent *bev, short event, void *arg)
  118. {
  119. if (event & BEV_EVENT_EOF)
  120. {
  121. printf("connection closed\n");
  122. }
  123. else if (event & BEV_EVENT_ERROR)
  124. {
  125. printf("some other error\n");
  126. }
  127. else if( event & BEV_EVENT_CONNECTED)
  128. {
  129. printf("the client has connected to server\n");
  130. evutil_socket_t fd = bufferevent_getfd(bev);
  131. set_tcp_no_delay(fd);
  132. }
  133. }
  134. /*
  135. **@author: laoyang360
  136. **@date: 20161211
  137. **@param: int iPort, 传入端口。
  138. **@brief: libevent,socket初始化等
  139. */
  140. void YuLibeventClient::init(int iPort)
  141. {
  142. WSADATA wsaData;
  143. DWORD Ret;
  144. if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
  145. {
  146. printf("WSAStartup failed with error %d\n", Ret);
  147. exit(1);
  148. }
  149. m_timeout.tv_sec = s_iTimeOut; //60s超时
  150. m_timeout.tv_usec = 0;
  151. m_pszMsg = (char*)malloc(1 + s_iBlockSize);
  152. memset(m_pszMsg, 0, s_iBlockSize);
  153. for (int i = 0; i < s_iBlockSize; ++i)
  154. {
  155. m_pszMsg[i] = 't'; /*i%128;*/
  156. }
  157. m_pszMsg[s_iBlockSize] = '\0';
  158. //printf("m_pszMsg = %s\n", m_pszMsg);
  159. m_pBase = event_base_new();
  160. if (!m_pBase)
  161. {
  162. printf("Couldn't open event base!\n");
  163. exit(1);
  164. }
  165. //设定超时
  166. m_evtimeout = evtimer_new(m_pBase, timeoutcb, m_pBase);
  167. evtimer_add(m_evtimeout, &m_timeout);
  168. struct sockaddr_in server_addr;
  169. memset(&server_addr, 0, sizeof(server_addr) );
  170. server_addr.sin_family = AF_INET;
  171. server_addr.sin_port = htons(iPort);
  172. server_addr.sin_addr.s_addr = inet_addr(s_serverIpAddr);
  173. m_bevs = (bufferevent**)malloc(s_iSessionCnt * sizeof(struct bufferevent *));
  174. for (int i=0; i < s_iSessionCnt; ++i)
  175. {
  176. struct bufferevent* bev = bufferevent_socket_new(m_pBase, -1, BEV_OPT_CLOSE_ON_FREE);
  177. bufferevent_setcb(bev, readcb, NULL, eventcb, NULL);
  178. bufferevent_enable(bev, EV_READ | EV_WRITE);
  179. evbuffer_add(bufferevent_get_output(bev), m_pszMsg, s_iBlockSize);
  180. if (bufferevent_socket_connect(bev, (struct sockaddr *)&server_addr,
  181. sizeof(server_addr)) < 0)
  182. {
  183. printf("Error starting connection!\n");
  184. bufferevent_free(bev);
  185. exit(1);
  186. }
  187. m_bevs[i] = bev;
  188. }
  189. }
  190. /*
  191. **@author: laoyang360
  192. **@date: 20161211
  193. **@param: 无
  194. **@brief: 启动,循环执行
  195. */
  196. void YuLibeventClient::start()
  197. {
  198. event_base_dispatch(m_pBase);
  199. }
  200. /*
  201. **@author: laoyang360
  202. **@date: 20161211
  203. **@param: 无
  204. **@brief: 停止,内存等释放&结果统计
  205. */
  206. void YuLibeventClient::stop()
  207. {
  208. //evconnlistener_free(m_pListener);
  209. if (NULL != m_pBase)
  210. {
  211. event_base_free(m_pBase);
  212. }
  213. for (int i = 0; i < s_iSessionCnt; ++i)
  214. {
  215. if (NULL != m_bevs[i])
  216. {
  217. bufferevent_free(m_bevs[i]);
  218. }
  219. }
  220. if (NULL != m_bevs)
  221. {
  222. free(m_bevs);
  223. }
  224. if (NULL != m_pszMsg)
  225. {
  226. free(m_pszMsg);
  227. }
  228. printf("%d total bytes read\n", m_siLtotal_bytes_read);
  229. printf("%d total messages read\n", m_siLtotal_messages_read);
  230. printf("%.3f average messages size read\n", (double)m_siLtotal_bytes_read/m_siLtotal_messages_read);
  231. printf("%.3f MiB/s throughtput\n", (double)m_siLtotal_bytes_read/(m_timeout.tv_sec * 1024 * 1024));
  232. }

4.测试效果如下

5.源码包下载

http://download.csdn.net/detail/wojiushiwo987/9708418

后记: 
项目中用到了libevent,但我自己一直没有总结这块,从去年开始到现在这个想法持续了一年,总算了了心愿。 
代码对大牛陈硕的C的测试代码进行了C++的封装、测试、验证。向大牛的钻研精神和毅力学习和致敬!

参考: 
http://blog.csdn.net/solstice/article/details/6527585 
https://github.com/chenshuo/recipes/tree/master/pingpong/libevent 
http://blog.csdn.net/funkri/article/details/9352955 
http://blog.csdn.net/laoyang360/article/details/8675922

2016年12月11日 22:16 思于宾馆

作者:铭毅天下 
转载请标明出处,原文地址: 
http://blog.csdn.net/laoyang360/article/details/53576132 
如果感觉本文对您有帮助,请点击‘顶’支持一下,您的支持是我坚持写作最大的动力,谢谢!

Windows下libevent C++封装类实现(为什么要使用封装好的网络库?)的更多相关文章

  1. Windows下libevent C++封装类实现

    题记 windows平台下对于服务器高并发的网络模型选型中,使用libevent是个不错的选择. 本文的背景基于:国内博客对于libevent大多介绍linux实现,大多是c语言的实现,Windows ...

  2. windows下libevent的编译及使用

    之前简单分析了libevent的源码,过了一段时间要用的时候发现完全忘记了..从头记录一下流程 1.编译 可以从github下载libevent的压缩包,解压后 修改以下三个文件,添加宏定义: 在以下 ...

  3. 【VS开发】【数据库开发】windows下libevent x64库静态编译

    按照libevent的文档,使用VC的nmake -f Makefile.nmake即可编译32位release模式.因为项目中要求编译64位的版本,需要在Makefile.nmake中添加一个LIB ...

  4. Apache Thrift 在Windows下的安装与开发

    Windows下安装Thrift框架的教程很多.本文的不同之处在于,不借助Cygwin或者MinGW,只用VS2010,和Thrift官网下载的源文件,安装Thrift并使用. 先从官网 下载这两个文 ...

  5. Linux下Libevent安装和简单实用

    前言 Libevent 是一个用C语言编写的.轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大:源代码相 ...

  6. windows下的socket网络编程

    windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了, ...

  7. windows下的socket网络编程(入门级)

    windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先 ...

  8. Windows下编译安装 FFmpeg

    在Linux/Mac下编译 ffmpeg是非常方便的.但要在 Windows下编译 ffmpeg还真要花点时间.以下就是在 Windowns下编译ffmpeg的步骤: 一.安装Cygwin 在wind ...

  9. windows下CEF3的关闭流程《转》

    原文地址:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF_Close.md ============================ ...

随机推荐

  1. 【t076】竞赛排名

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 某市组织了一次中学生科技全能竞赛,每个选手要参加数学.物理.化学.天文.地理.生物.计算机和英语共八项 ...

  2. js Map 遍历

    1. 在知道的key的情况下遍历map自然就跟数组一样的访. 2.在不知道key的情况下遍历map(此情况更多吧)    2.1  for(var key in Map){  };  //第一种   ...

  3. hadoop 3.x 单机集群配置/启动时的问题处理

    一.修改配置文件(hadoop目录/etc/hadoop/配置文件) 1.修改hadoop-env.sh,指定JAVA_HOME 修改完毕后 2.修改core-site.xml <configu ...

  4. 手动安装MySQL8.0

    首先跟大家唠一唠家常,随着MySQL迅速的更新,MySQL突飞猛进已经更新到了8.0版本,那么它和我们之前用的5.X版本有什么明显的区别那? 首先给大家看下MySQL5.X自带表的查询速度 之后献上M ...

  5. 利用WPF建立自己的3d gis软件(非axhost方式)(六)跳转,增加外部三维模型

    原文:利用WPF建立自己的3d gis软件(非axhost方式)(六)跳转,增加外部三维模型 先下载SDK:https://pan.baidu.com/s/1M9kBS6ouUwLfrt0zV0bPe ...

  6. java项目采用exe4j打包成exe档

    java项目采用exe4j打包成exe档 前言:我们都知道java是平台无关性.能够打包成jar文件,到不论什么操作系统有jre环境的电脑都能够同意!可是我们打包成exe文件就相当于舍弃了这一大优势, ...

  7. Leetcode 237 Delete Node in a Linked List 链表

    如题 删除是要注意让现在的链表等于下一个链表的值 class Solution { public: void deleteNode(ListNode* node) { ListNode *nextno ...

  8. WPF 圆角textbox

    原文:WPF 圆角textbox 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a771948524/article/details/9245965 ...

  9. 【树转数组】poj1195

    /* 二维的树状数组: 更新某个元素时: NO.1:c[n1],c[n2],c[n3],....,c[nm]; 当中n1 = i,n(i+1) = ni+lowbit(ni); nm+lowbit(n ...

  10. ASP.NET Core 属性路由 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 属性路由 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 属性路由 经过前面章节的学习,想必你已经对 ASP.NET Core ...