• linux实现基础网络库(socket,epoll,pthread,cmake,pipe, sem,codition,)


    面试时经常会问到网络库,好久没看过这块知识了,实现一下,用到了一下一些知识点

    1. socket搭建网络库必须用到的
    2. epoll 多路复用技术用的是epoll
    3. pthread 服务器用到了多线程,主线程启动服务器,子线程处理来自各个连接的数据
    4. pipe 用在进程间通讯 0是读 1是写
    5. sem 信号 用在进程间通讯
    6. pthread_con_t 条件变量,用于进程间通讯
    7. cmake 用来编译工程

    下面是服务器代码:epollserver.cpp

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. using namespace std;
    14. typedef struct sockaddr_in sockaddr_in;
    15. sem_t sem; // 信号
    16. pthread_cond_t cond; // 条件变量
    17. pthread_mutex_t mutex; // 互斥锁
    18. int pipeHandle[2]; // 管道 0读端, 1写端
    19. void set_non_blocking(int sock)
    20. {
    21. int flag;
    22. flag = fcntl(sock, F_GETFL);
    23. if (flag < 0) {
    24. cout << "error fcntl(sock, GETFL)! " << endl;
    25. return;
    26. }
    27. flag |= O_NONBLOCK;
    28. if (fcntl(sock, F_SETFL, flag) < 0) {
    29. cout << "error fcntl(sock, F_SETFL, opts)! " << endl;
    30. return;
    31. }
    32. }
    33. void* voteAction(void* data) {
    34. while (1)
    35. {
    36. pthread_cond_wait(&cond, &mutex); // 无条件等待,与互斥锁配合防止多个线程同时调用
    37. pthread_t senderId;
    38. read(pipeHandle[0], &senderId, sizeof(senderId));
    39. cout << "vote action: people " << senderId << " vote action happen, please call 110" << endl;
    40. write(pipeHandle[1], &senderId, sizeof(senderId));
    41. sem_post(&sem);
    42. }
    43. return NULL;
    44. }
    45. void* policeCenter(void* data)
    46. {
    47. while (1) {
    48. sem_wait(&sem);
    49. pthread_t senderId;
    50. read(pipeHandle[0], &senderId, sizeof(senderId));
    51. cout << "110 center recevice people" << senderId << " notify vote event happened" << endl;
    52. }
    53. return NULL;
    54. }
    55. struct my_params {
    56. int main_listenfd;
    57. int con_fd;
    58. int sock_event;
    59. int epoll_fd;
    60. };
    61. void* recvfromclient(void* args)
    62. {
    63. // cout << "thread pod = " << gettid() << endl;
    64. struct my_params* params;
    65. params = (struct my_params*)args;
    66. int listenfd = (*params).main_listenfd;
    67. int co_fd = (*params).con_fd;
    68. int epfd = (*params).epoll_fd;
    69. int events = (*params).sock_event;
    70. struct epoll_event ev;
    71. int sockfd;
    72. int socklen = sizeof(struct sockaddr_in);
    73. struct sockaddr_in client_addr;
    74. char buffer[1024];
    75. if (co_fd == listenfd)
    76. {
    77. cout << "accept connection, fd is " << listenfd << endl;
    78. int connfd = accept(listenfd, (struct sockaddr*)&client_addr, (socklen_t*)&socklen);
    79. if (connfd < 0)
    80. {
    81. cout << "connect fd < 0" << endl;
    82. pthread_exit(NULL);
    83. return NULL;
    84. }
    85. set_non_blocking(connfd);
    86. char* str = inet_ntoa(client_addr.sin_addr);
    87. cout << "connect from " << str << endl;
    88. ev.data.fd = connfd;
    89. ev.events = EPOLLIN | EPOLLET;
    90. epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
    91. }
    92. else if (events & EPOLLIN)
    93. {
    94. sockfd = co_fd;
    95. if (sockfd < 0)
    96. {
    97. cout << "epoll in sockfd < 0" << endl;
    98. pthread_exit(NULL);
    99. return NULL;
    100. }
    101. memset(buffer, 0, sizeof(buffer));
    102. int ret = recv(sockfd, buffer, sizeof(buffer), 0);
    103. if (ret < 0)
    104. {
    105. cout << "recv error" << endl;
    106. }
    107. else if (ret == 0) // 对端主动关闭连接是可读事件,需要处理发送改过来的FIN包,对应的是read返回0
    108. {
    109. close(sockfd);
    110. sockfd = -1;
    111. cout << inet_ntoa(client_addr.sin_addr) << "closed" << endl;
    112. return NULL;
    113. }
    114. if (string(buffer) == "exit")
    115. {
    116. cout << inet_ntoa(client_addr.sin_addr) << " closed connect" << endl;
    117. close(sockfd);
    118. sockfd = -1;
    119. return NULL;
    120. }
    121. if (string(buffer) == "vote") // 报警
    122. {
    123. pthread_t pid = gettid();
    124. write(pipeHandle[1], &pid, sizeof(pid));
    125. pthread_cond_signal(&cond); // 唤醒条件变量
    126. }
    127. cout << "receive :" << buffer << endl;
    128. ev.data.fd = sockfd;
    129. ev.events = EPOLLOUT | EPOLLET;
    130. epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
    131. }
    132. else if (events & EPOLLOUT)
    133. {
    134. sockfd = co_fd;
    135. strcpy(buffer, "ok");
    136. int ret = send(sockfd, buffer, strlen(buffer), 0);
    137. if (ret <= 0)
    138. {
    139. cout << "send error" << endl;
    140. pthread_exit(NULL);
    141. return NULL;
    142. }
    143. cout << "send: " << buffer << endl;
    144. ev.data.fd = sockfd;
    145. ev.events = EPOLLIN | EPOLLET;
    146. epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
    147. }
    148. pthread_exit(NULL);
    149. return NULL;
    150. }
    151. int main()
    152. {
    153. int listenfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET 协议族,ipv4协议 SOCK_STREAM tcp链接,提供序列化的,可靠的,双向连接的字节流, 第三个参数是指定协议,0自动选择type类型对应的默认协议
    154. if (listenfd == -1)
    155. {
    156. cout << "socket create fail" << endl;
    157. return -1;
    158. }
    159. set_non_blocking(listenfd);
    160. struct epoll_event ev, events[20]; // ev用于注册事件,数组用于回传要处理的事件
    161. int epfd = epoll_create(256);
    162. ev.data.fd = listenfd;
    163. ev.events = EPOLLIN | EPOLLET;
    164. epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
    165. struct sockaddr_in serveraddr; //sockaddr_in 分别将端口和地址存储在两个结构体中
    166. memset(&serveraddr, 0, sizeof(serveraddr));
    167. serveraddr.sin_family = AF_INET;
    168. serveraddr.sin_port = htons(8000);
    169. serveraddr.sin_addr.s_addr = INADDR_ANY;
    170. if (bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) != 0)
    171. {
    172. cout << "bind error" << endl;
    173. return -1;
    174. }
    175. if (listen(listenfd, 5) != 0)
    176. {
    177. cout << "Listen error" << endl;
    178. close(listenfd);
    179. return -1;
    180. }
    181. pthread_t voteThread;
    182. int res = pthread_create(&voteThread, NULL, voteAction, NULL); // 创建一个报警线程
    183. if (res < 0)
    184. {
    185. cout << "crete vote thread fail" << endl;
    186. close(listenfd);
    187. return -1;
    188. }
    189. pthread_cond_init(&cond, NULL); // 动态创建条件变量
    190. pthread_mutex_init(&mutex, NULL); // 动态创建互斥锁
    191. pthread_t policeThread;
    192. res = pthread_create(&policeThread, NULL, policeCenter, NULL);
    193. if (res < 0)
    194. {
    195. cout << "crete police center thread fail" << endl;
    196. close(listenfd);
    197. return -1;
    198. }
    199. sem_init(&sem, 1, 0); // 信号量
    200. res = pipe(pipeHandle); // 管道
    201. if (res < 0)
    202. {
    203. cout << "create pipe fail" << endl;
    204. close(listenfd);
    205. return -1;
    206. }
    207. cout << "*******************************welcome connect to server******************************" << endl;
    208. while (1)
    209. {
    210. int nfds = epoll_wait(epfd, events, 20, 1000);
    211. if (nfds > 0)
    212. {
    213. for (int i = 0; i < nfds; i++)
    214. {
    215. struct my_params param;
    216. param.main_listenfd = listenfd;
    217. param.con_fd = events[i].data.fd;
    218. param.sock_event = events[i].events;
    219. param.epoll_fd = epfd;
    220. pthread_t thread;
    221. res = pthread_create(&thread, NULL, recvfromclient, (void*)¶m);
    222. if (res < 0)
    223. {
    224. cout << "thread create fail" << endl;
    225. continue;
    226. }
    227. res = pthread_detach(thread); // 线程分离状态,该线程结束后,其退出状态不由其他线程获取,而字何解自己自动释放清理pcb的残留资源(进程没有该机制)
    228. if (res < 0)
    229. {
    230. cout << "thread deatch fail" << endl;
    231. }
    232. }
    233. }
    234. }
    235. pthread_join(voteThread, NULL);
    236. pthread_join(policeThread, NULL);
    237. close(listenfd);
    238. return 0;
    239. }

    以下是服务器的编译文件:CMakeLists.txt

    1. cmake_minimum_required(VERSION 3.16.3)
    2. project(epollserver LANGUAGES CXX)
    3. link_libraries(pthread)
    4. add_executable(server epollserver.cpp)

    以上文件都是服务器端的代码,需要放在同一个目录下

    以下是客户端代码:epollclient.cpp

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. using namespace std;
    9. int main()
    10. {
    11. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    12. if (sockfd <= 0)
    13. {
    14. cout << "socket error";
    15. return -1;
    16. }
    17. struct sockaddr_in remote_addr;
    18. memset(&remote_addr, 0, sizeof(remote_addr));
    19. remote_addr.sin_family = AF_INET;
    20. remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    21. remote_addr.sin_port = htons(8000);
    22. if (connect(sockfd, (struct sockaddr*)&remote_addr, sizeof(struct sockaddr)) < 0)
    23. {
    24. cout << "connect eror";
    25. return -1;
    26. }
    27. cout << "connected to server" << endl;
    28. char buffer[1024];
    29. while (1)
    30. {
    31. memset(buffer, 0, sizeof(buffer));
    32. cout << "please enter message:";
    33. cin >> buffer;
    34. int len = send(sockfd, buffer, strlen(buffer), 0);
    35. if (len <= 0)
    36. {
    37. cout << "send error" << endl;
    38. break;
    39. }
    40. if (string(buffer) == "exit")
    41. {
    42. cout << "good bye" << endl;
    43. break;
    44. }
    45. memset(buffer, 0, sizeof(buffer));
    46. len = recv(sockfd, buffer, 256, 0);
    47. if (len > 0)
    48. {
    49. buffer[len] = '\0';
    50. cout << "Received:" << buffer << endl;
    51. }
    52. }
    53. close(sockfd);
    54. return 0;
    55. }

    以下是客户端的编译文件:CMakeLists.txt

    1. cmake_minimum_required(VERSION 3.16.3)
    2. project(epollclient)
    3. add_executable(client epollclient.cpp)

    如果不会使用Cmake 可以直接使用命令

    客户端命令编译:g++ epollclient.cpp -o client -lpthread

    服务器命令编译:g++ epollserver.cpp -o server -lpthread

  • 相关阅读:
    【vue】vue 中插槽的三种类型:
    Windows安装redis
    Business Objects单一报表升级为全套商业智能BI产品,探索更多平台与行业
    [2023.09.12]: Yew应用开发的第一个hook--use_state
    Spring Bean生命周期|分析|图解
    6 Redis的慢查询&配置
    Nessus已激活,New Scan按钮不可点击
    4S店汽车行业万能通用小程序源码系统 在线预约试驾+购车计算器 源码完全开源可二次开发
    15. SAP ABAP OData 服务里 EntityType 和 EntitySet 的区别
    【STL】平衡二叉树
  • 原文地址:https://blog.csdn.net/choudan8888/article/details/134033823