欢迎来到飞鸟慕鱼博客,开始您的技术之旅!
当前位置: 首页知识笔记正文

linux实现基础网络库

墨初 知识笔记 47阅读

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

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

下面是服务器代码epollserver.cpp

#include<stdio.h>#include<iostream>#include<cstring>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<unistd.h>#include<fcntl.h>#include<sys/epoll.h>#include<errno.h>#include<pthread.h>#include <semaphore.h>using namespace std;typedef struct sockaddr_in sockaddr_in;sem_t sem;// 信号pthread_cond_t cond; // 条件变量pthread_mutex_t mutex; // 互斥锁int pipeHandle[2];// 管道 0读端 1写端void set_non_blocking(int sock){int flag;flag  fcntl(sock, F_GETFL);if (flag < 0) {cout << error fcntl(sock, GETFL)!  << endl;return;}flag | O_NONBLOCK;if (fcntl(sock, F_SETFL, flag) < 0) {cout << error fcntl(sock, F_SETFL, opts)!  << endl;return;}}void* voteAction(void* data) {while (1){pthread_cond_wait(&cond, &mutex);// 无条件等待与互斥锁配合防止多个线程同时调用pthread_t senderId;read(pipeHandle[0], &senderId, sizeof(senderId));cout << vote action: people  << senderId <<  vote action happen, please call 110 << endl;write(pipeHandle[1], &senderId, sizeof(senderId));sem_post(&sem);}return NULL;}void* policeCenter(void* data){while (1) {sem_wait(&sem);pthread_t senderId;read(pipeHandle[0], &senderId, sizeof(senderId));cout << 110 center recevice people << senderId <<  notify vote event happened << endl;}return NULL;}struct my_params {int main_listenfd;int con_fd;int sock_event;int epoll_fd;};void* recvfromclient(void* args){// cout << thread pod   << gettid() << endl;struct my_params* params;params  (struct my_params*)args;int listenfd  (*params).main_listenfd;int co_fd  (*params).con_fd;int epfd  (*params).epoll_fd;int events  (*params).sock_event;struct epoll_event ev;int sockfd;int socklen  sizeof(struct sockaddr_in);struct sockaddr_in client_addr;char buffer[1024];if (co_fd  listenfd){cout << accept connection, fd is  << listenfd << endl;int connfd  accept(listenfd, (struct sockaddr*)&client_addr, (socklen_t*)&socklen);if (connfd < 0){cout << connect fd < 0 << endl;pthread_exit(NULL);return NULL;}set_non_blocking(connfd);char* str  inet_ntoa(client_addr.sin_addr);cout << connect from  << str << endl;ev.data.fd  connfd;ev.events  EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);}else if (events & EPOLLIN){sockfd  co_fd;if (sockfd < 0){cout << epoll in sockfd < 0 << endl;pthread_exit(NULL);return NULL;}memset(buffer, 0, sizeof(buffer));int ret  recv(sockfd, buffer, sizeof(buffer), 0);if (ret < 0){cout << recv error << endl;}else if (ret  0)// 对端主动关闭连接是可读事件需要处理发送改过来的FIN包,对应的是read返回0{close(sockfd);sockfd  -1;cout << inet_ntoa(client_addr.sin_addr) << closed << endl;return NULL;}if (string(buffer)  exit){cout << inet_ntoa(client_addr.sin_addr) <<  closed connect << endl;close(sockfd);sockfd  -1;return NULL;}if (string(buffer)  vote)// 报警{pthread_t pid  gettid();write(pipeHandle[1], &pid, sizeof(pid));pthread_cond_signal(&cond);// 唤醒条件变量}cout << receive : << buffer << endl;ev.data.fd  sockfd;ev.events  EPOLLOUT | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);}else if (events & EPOLLOUT){sockfd  co_fd;strcpy(buffer, ok);int ret  send(sockfd, buffer, strlen(buffer), 0);if (ret < 0){cout << send error << endl;pthread_exit(NULL);return NULL;}cout << send:  << buffer << endl;ev.data.fd  sockfd;ev.events  EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);}pthread_exit(NULL);return NULL;}int main(){int listenfd  socket(AF_INET, SOCK_STREAM, 0);// AF_INET 协议族ipv4协议  SOCK_STREAM tcp链接提供序列化的可靠的双向连接的字节流 第三个参数是指定协议0自动选择type类型对应的默认协议if (listenfd  -1){cout << socket create fail << endl;return -1;}set_non_blocking(listenfd);struct epoll_event ev, events[20];// ev用于注册事件数组用于回传要处理的事件int epfd  epoll_create(256);ev.data.fd  listenfd;ev.events  EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);struct sockaddr_in serveraddr;  //sockaddr_in 分别将端口和地址存储在两个结构体中memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family  AF_INET;serveraddr.sin_port  htons(8000);serveraddr.sin_addr.s_addr  INADDR_ANY;if (bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) ! 0){cout << bind error << endl;return -1;}if (listen(listenfd, 5) ! 0){cout << Listen error << endl;close(listenfd);return -1;}pthread_t voteThread;int res  pthread_create(&voteThread, NULL, voteAction, NULL);// 创建一个报警线程if (res < 0){cout << crete vote thread fail << endl;close(listenfd);return -1;}pthread_cond_init(&cond, NULL);// 动态创建条件变量pthread_mutex_init(&mutex, NULL);// 动态创建互斥锁pthread_t policeThread;res  pthread_create(&policeThread, NULL, policeCenter, NULL);if (res < 0){cout << crete police center thread fail << endl;close(listenfd);return -1;}sem_init(&sem, 1, 0);// 信号量res  pipe(pipeHandle);// 管道if (res < 0){cout << create pipe fail << endl;close(listenfd);return -1;}cout << *******************************welcome connect to server****************************** << endl;while (1){int nfds  epoll_wait(epfd, events, 20, 1000);if (nfds > 0){for (int i  0; i < nfds; i){struct my_params param;param.main_listenfd  listenfd;param.con_fd  events[i].data.fd;param.sock_event  events[i].events;param.epoll_fd  epfd;pthread_t thread;res  pthread_create(&thread, NULL, recvfromclient, (void*)&param);if (res < 0){cout << thread create fail << endl;continue;}res  pthread_detach(thread);// 线程分离状态该线程结束后其退出状态不由其他线程获取而字何解自己自动释放清理pcb的残留资源进程没有该机制if (res < 0){cout << thread deatch fail << endl;}}}}pthread_join(voteThread, NULL);pthread_join(policeThread, NULL);close(listenfd);return 0;}

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

cmake_minimum_required(VERSION 3.16.3)project(epollserver LANGUAGES CXX)link_libraries(pthread)add_executable(server epollserver.cpp)

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

以下是客户端代码epollclient.cpp

#include<iostream>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<cstring>using namespace std;int main(){int sockfd  socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cout << socket error;return -1;}struct sockaddr_in remote_addr;memset(&remote_addr, 0, sizeof(remote_addr));remote_addr.sin_family  AF_INET;remote_addr.sin_addr.s_addr  inet_addr(127.0.0.1);remote_addr.sin_port  htons(8000);if (connect(sockfd, (struct sockaddr*)&remote_addr, sizeof(struct  sockaddr)) < 0){cout << connect eror;return -1;}cout << connected to server << endl;char buffer[1024];while (1){memset(buffer, 0, sizeof(buffer));cout << please enter message:;cin >> buffer;int len  send(sockfd, buffer, strlen(buffer), 0);if (len < 0){cout << send error << endl;break;}if (string(buffer)   exit){cout << good bye << endl;break;}memset(buffer, 0, sizeof(buffer));len  recv(sockfd, buffer, 256, 0);if (len > 0){buffer[len]  \0;cout << Received: << buffer << endl;}}close(sockfd);return 0;}

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

cmake_minimum_required(VERSION 3.16.3)project(epollclient)add_executable(client epollclient.cpp)

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

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

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

标签:
声明:无特别说明,转载请标明本文来源!