C++编程 (三)--- 深入C++后台开发
2015-10-28 18:11
627 查看
搞了很久搜索了,可是做的很多都是业务逻辑和PM的需求,也没有高大上的技术。感觉随着开源项目的成熟技术的门槛在逐渐的降低,曾经高大上的技术已经渐渐变得没什么门槛了。。。(记得脉脉上看到一句很好玩的话,程序员是一个高大上的职业,直到JAVA语言的出现。。。)不过我也还是认真总结和实践了一些深入的技术,在做业务的时候也有一些积累的吧。总的来说C++后台开发深入一些的有网络编程、多线程编程、进程/线程同步/通信和调度、动态链接库使用、常用的框架的深入阅读和理解、常用的运行时程序问题排查(内存泄露、无法响应新的请求)、分布式系统的使用、高并发系统优化。所以本文一共分为如下十一个部分:
一、网络编程
二、多线程编程
三、多进程/线程同步/通信/调度
四、动态链接库使用
五、开源框架的深入阅读和理解(以thrift为例)
六、常用运行时程序问题排查
七、分布式系统问题
八、高并发系统的优化
九、代码风格和设计模式
十、C++语言的新特性
十一、Linux内核知识
一、网络编程:
1. TCP与UDP:
a. TCP与UDP报头
1). TCP报头(最少20个字节):源端口、目的端口、序号、确认号、数据偏移、ACK、RST、SYN、FIN、窗口、校检和、紧急指针、选项、填充。
2). UDP报头:(8个字节)预案端口、目的端口、用户数据包长度、校检和、数据。
b. TCP与UDP的区别
1). TCP是面向连接的服务,有拥塞控制和超时重传,因此有滑动窗口。
2). UDP是非面向连接的服务,支持一对多通信,如广播。
2. TCP 3次握手、4次挥手过程:
a. TCP三次握手、四次挥手漏洞分析,天网如何使用TCP协议中断翻墙协议访问。
为什么要进行三次握手?因为双方要交换序列号和窗口大小,发送方确认接收方接收到了syn请求,防止出现死锁。
TCP三次握手的漏洞,SYN FLOOD攻击:客户端不停的伪造IP来给服务端发起请求,服务端对每个syn都要分配一个TCB,通常每个TCB至少280个字节。应对:syn cookie技术,使用双方通信信息、MSS、时间等计算,看看与对方回报文中的sequence number是否相同。
TCP四次挥手?因为TCP连接有个半连接状态,假设AB要释放,那么A发送了一个释放请求给B,B立即回复确认。但在此之间B发送的数据A依然需要接受,B需要回复给A它不再发送数据了。
为什么TIME_WAIT需要有2MSL的时间?为了避免最后一个ACK没有被接收到,预留重发时间。
3. 多路IO复用模型:
a. 阻塞、非阻塞:应用程序的调用是否立即返回!
b. 异步、同步:数据拷贝的时候进程是否阻塞!
c. select、poll、epoll
1). 三种IO复用模型对比:
select支持最大打开文件数目有限(一般select使用32个32位整数作为文件描述符集)、用户态数据需要拷贝到内核态、每次都需要线性遍历每个FD,速度太慢;
poll最大打开fd数目不限;epoll克服了上面所有的缺点,但是如果每个连接都是活跃的,效率也不高。
2). select:
select执行流程:
a). 设置maxfd,将fd加入select监控集,使用一个array保存放到select监控集中的d,一是用于在select返回后,array作为源数据和fdset进行fd_isset判断。二是在select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select都要从array取得fd逐一加入。(select模型必须在select前循环array(加fd,取maxfd),返回后循环array。)
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
参数:
sockfd: socket 函数返回的套接字描述符
readfds : 读描述符集合
writefds: 写描述符集合
errorfds: 错误描述符集合
timeout: 超时
返回值
成功:返回值 0:无 >0:描述符就绪的总位数
错误:返回INVALID_SOCKET(-1)
示例:
b). int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:
fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;
返回值:
>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的 socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
-1: poll函数调用失败,同时会自动设置全局变量errno;
示例:
c). int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event );
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
epoll_create生成一个 Epoll 专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的 socket fd 上是否发生以及发生了什么事件。 size 就是你在这个 Epoll fd 上能关注的最大 socket fd 数,大小自定,只要内存足够。
epoll_ctl控制某个 Epoll 文件描述符上的事件:注册、修改、删除。其中参数 epfd 是 epoll_create() 创建 Epoll 专用的文件描述符。相对于 select 模型中的 FD_SET 和 FD_CLR 宏。op:EPOLL_CTL_ADD Register the target file descriptor fd on the epoll instance, EPOLL_CTL_MOD Change the event event associated with the target
file descriptor fd, EPOLL_CTL_DEL Remove (deregister) the target file descriptor fd from the epoll instance。
4. 网络协议分层模型:
5. 其他一些小问题:
a. 网络模型直接排序一般采用大端存储的。
b. keepalive???:
c. 长连接、短连接:
d. 防火墙如何利用TCP协议终止你的翻墙请求?
6. 网络分层模型和不同层的支持的协议:
(如何画时序图)
二、多线程编程:
1. 线程与进程:
线程共享:进程指令、大多数数据、打开的文件、信号处理函数和信号处置、当前工作目录、用户id和组ID
进程共享:线程ID、寄存器集合(程序计数器和函数指针)、栈、errno、信号掩码、优先级
线程&进程:线程是系统调度的最小单元,fork需要把父进程内存影响复制到子进程;fork返回以后父子进程需要进行IPC通信。
2. 多线程编程:
pthread_create( pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void arg);
pthread_join(pthread_t *tid, void **status) //如果status指针非空,把线程的返回值、一个指向某个对象的指针存入status指向的位置
pthread_self(void) // 线程自身id
pthread_detach(tid)// 线程终止时,所有资源被释放
pthread_exit(void *status)//线程终止
3. 多进程编程:
fork()//创建子进程
exec()//4个函数族。创建进程,但子进程执行时废弃当前进程的数据段和堆栈段
4. 进程/线程间同步\通信:
一般我们讨论进程间共享数据,线程间同步。因为线程很多数据都是共有的,而进程间很难共享数据,因此线程的同步和进程的数据共享一般都是我们面临的问题的。
a. 进程间共享/通信数据(IPC)方法:
管道、FIFO、共享内存、消息队列、信号、socket
对比:
1. 管道:速度慢、容量有限、只有父子进程通讯
2. FIFO: 任何进程都能通信,但速度慢
3. 消息队列:容量受限
4. 信号量:不能传递复杂信息
5. 共享内存:速度快、容量大。需要注意同步
1).
b. 线程间同步方法(SYNC):
临界区、锁、信号量、事件、interlocked variable
1). 锁:
pthread_mutex_t counter_mutex=PTHREAD_MUTEX_INITIALIZER; COND
pthread_mutex_lock(pthread_mutex_t * mptr);
// do something
pthread_mutex_unlock(pthread_mutex_t *mptr);
http://www.cnblogs.com/memewry/archive/2012/08/22/2651696.html
五、开源框架深入阅读和理解:
1. thrift协议的数据类型、协议、传输、服务类型
2. thrift协议源代码阅读
六、常用运行时程序排查:
1. 使用cp替换so文件为什么服务会core?
2. 如何排查内存泄露
1. 智能指针 std::shared_ptr 用法:
a). 智能指针是用来实现指针对象的共享和内存生存期自动管理(一般使用引用计数实现,我理解智能指针就是一个栈对象,在智能指针的生命期结束时,对智能指针指向的动态内存使用减1,如果计数减为0,则释放内存)。
b). 所有的智能指针都会重载* -> 等符号。
b). 使用智能指针注意事项:
a. 不要把原生指针给多个shared_ptr管理。
b. 不要在函数实参里创建shared_ptr。
c. shared_ptr作为被保护对象时小心循环引用。
d. 不要把this指针传给shared_ptr。
c). static_ptr_cast ???:
2. auto用法:
参考文献:
1. Unix网络编程:
2. thrift服务源代码分析: http://yanyiwu.com/work/2014/12/06/thrift-tnonblockingserver-analysis.html
3.
一、网络编程
二、多线程编程
三、多进程/线程同步/通信/调度
四、动态链接库使用
五、开源框架的深入阅读和理解(以thrift为例)
六、常用运行时程序问题排查
七、分布式系统问题
八、高并发系统的优化
九、代码风格和设计模式
十、C++语言的新特性
十一、Linux内核知识
一、网络编程:
1. TCP与UDP:
a. TCP与UDP报头
1). TCP报头(最少20个字节):源端口、目的端口、序号、确认号、数据偏移、ACK、RST、SYN、FIN、窗口、校检和、紧急指针、选项、填充。
2). UDP报头:(8个字节)预案端口、目的端口、用户数据包长度、校检和、数据。
b. TCP与UDP的区别
1). TCP是面向连接的服务,有拥塞控制和超时重传,因此有滑动窗口。
2). UDP是非面向连接的服务,支持一对多通信,如广播。
2. TCP 3次握手、4次挥手过程:
a. TCP三次握手、四次挥手漏洞分析,天网如何使用TCP协议中断翻墙协议访问。
为什么要进行三次握手?因为双方要交换序列号和窗口大小,发送方确认接收方接收到了syn请求,防止出现死锁。
TCP三次握手的漏洞,SYN FLOOD攻击:客户端不停的伪造IP来给服务端发起请求,服务端对每个syn都要分配一个TCB,通常每个TCB至少280个字节。应对:syn cookie技术,使用双方通信信息、MSS、时间等计算,看看与对方回报文中的sequence number是否相同。
TCP四次挥手?因为TCP连接有个半连接状态,假设AB要释放,那么A发送了一个释放请求给B,B立即回复确认。但在此之间B发送的数据A依然需要接受,B需要回复给A它不再发送数据了。
为什么TIME_WAIT需要有2MSL的时间?为了避免最后一个ACK没有被接收到,预留重发时间。
3. 多路IO复用模型:
a. 阻塞、非阻塞:应用程序的调用是否立即返回!
b. 异步、同步:数据拷贝的时候进程是否阻塞!
c. select、poll、epoll
1). 三种IO复用模型对比:
select支持最大打开文件数目有限(一般select使用32个32位整数作为文件描述符集)、用户态数据需要拷贝到内核态、每次都需要线性遍历每个FD,速度太慢;
poll最大打开fd数目不限;epoll克服了上面所有的缺点,但是如果每个连接都是活跃的,效率也不高。
2). select:
select执行流程:
a). 设置maxfd,将fd加入select监控集,使用一个array保存放到select监控集中的d,一是用于在select返回后,array作为源数据和fdset进行fd_isset判断。二是在select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select都要从array取得fd逐一加入。(select模型必须在select前循环array(加fd,取maxfd),返回后循环array。)
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
参数:
sockfd: socket 函数返回的套接字描述符
readfds : 读描述符集合
writefds: 写描述符集合
errorfds: 错误描述符集合
timeout: 超时
返回值
成功:返回值 0:无 >0:描述符就绪的总位数
错误:返回INVALID_SOCKET(-1)
示例:
<span style="font-size:12px;">/* 实现功能:通过select处理多个socket * 监听一个端口,监听到有链接时,添加到select的w. */ #include "select.h" #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/time.h> #include <netinet/in.h> typedef struct _CLIENT{ int fd; struct sockaddr_in addr; /* client's address information */ } CLIENT; #define MYPORT 59000 //最多处理的connect #define BACKLOG 5 //最多处理的connect CLIENT client[BACKLOG]; //当前的连接数 int currentClient = 0; //数据接受 buf #define REVLEN 10 char recvBuf[REVLEN]; //显示当前的connection void showClient(); int main() { int i, ret, sinSize; int recvLen = 0; fd_set readfds, writefds; int sockListen, sockSvr, sockMax; struct timeval timeout; struct sockaddr_in server_addr; struct sockaddr_in client_addr; for(i=0; i<BACKLOG; i++) { client[i].fd = -1; } //socket if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket error\n"); return -1; } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(MYPORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //bind if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { printf("bind error\n"); return -1; } //listen if(listen(sockListen, 5) < 0) { printf("listen error\n"); return -1; } for(i=0; i<BACKLOG; i++) { client[i].fd = -1; } //select while(1) { FD_ZERO(&readfds); FD_SET(sockListen, &readfds); sockMax = sockListen; //加入client for(i=0; i<BACKLOG; i++) { if(client[i].fd >0) { FD_SET(client[i].fd, &readfds); if(sockMax<client[i].fd) sockMax = client[i].fd; } } timeout.tv_sec=3; timeout.tv_usec=0; //select ret = select((int)sockMax+1, &readfds, NULL, NULL, &timeout); if(ret < 0) { printf("select error\n"); break; } else if(ret == 0) { printf("timeout ...\n"); continue; } printf("test111\n"); //读取数据 for(i=0; i<BACKLOG; i++) { if(client[i].fd>0 && FD_ISSET(client[i].fd, &readfds)) { if(recvLen != REVLEN) { while(1) { //recv数据 ret = recv(client[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0); if(ret == 0) { client[i].fd = -1; recvLen = 0; break; } else if(ret < 0) { client[i].fd = -1; recvLen = 0; break; } //数据接受正常 recvLen = recvLen+ret; if(recvLen<REVLEN) { continue; } else { //数据接受完毕 printf("%s, buf = %s\n", inet_ntoa(client[i].addr.sin_addr) , recvBuf); //close(client[i].fd); //client[i].fd = -1; recvLen = 0; break; } } } } } //如果可读 if(FD_ISSET(sockListen, &readfds)) { printf("isset\n"); sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr if(sockSvr == -1) { printf("accpet error\n"); } else { currentClient++; } for(i=0; i<BACKLOG; i++) { if(client[i].fd < 0) { client[i].fd = sockSvr; client[i].addr = client_addr; printf("You got a connection from %s \n",inet_ntoa(client[i].addr.sin_addr) ); break; } } //close(sockListen); } } printf("test\n"); return 0; } //显示当前的connection void showClient() { int i; printf("client count = %d\n", currentClient); for(i=0; i<BACKLOG; i++) { printf("[%d] = %d", i, client[i].fd); } printf("\n"); }</span>
b). int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:
fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;
返回值:
>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的 socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
-1: poll函数调用失败,同时会自动设置全局变量errno;
示例:
<span style="font-size:12px;">/* 实现功能:通过poll, 处理多个socket * 监听一个端口,监听到有链接时,添加到poll. */ #include "select.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <poll.h> #include <sys/time.h> #include <netinet/in.h> typedef struct _CLIENT{ int fd; struct sockaddr_in addr; /* client's address information */ } CLIENT; #define MYPORT 59000 //最多处理的connect #define BACKLOG 5 //当前的连接数 int currentClient = 0; //数据接受 buf #define REVLEN 10 char recvBuf[REVLEN]; #define OPEN_MAX 1024 int main() { int i, ret, sinSize; int recvLen = 0; fd_set readfds, writefds; int sockListen, sockSvr, sockMax; int timeout; struct sockaddr_in server_addr; struct sockaddr_in client_addr; struct pollfd clientfd[OPEN_MAX]; //socket if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket error\n"); return -1; } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(MYPORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //bind if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { printf("bind error\n"); return -1; } //listen if(listen(sockListen, 5) < 0) { printf("listen error\n"); return -1; } //clientfd 初始化 clientfd[0].fd = sockListen; clientfd[0].events = POLLIN; //POLLRDNORM; sockMax = 0; for(i=1; i<OPEN_MAX; i++) { clientfd[i].fd = -1; } //select while(1) { timeout=3000; //select ret = poll(clientfd, sockMax+1, timeout); if(ret < 0) { printf("select error\n"); break; } else if(ret == 0) { printf("timeout ...\n"); continue; } if (clientfd[0].revents & POLLIN)//POLLRDNORM { sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr if(sockSvr == -1) { printf("accpet error\n"); } else { currentClient++; } for(i=0; i<OPEN_MAX; i++) { if(clientfd[i].fd<0) { clientfd[i].fd = sockSvr; break; } } if(i==OPEN_MAX) { printf("too many connects\n"); return -1; } clientfd[i].events = POLLIN;//POLLRDNORM; if(i>sockMax) sockMax = i; } //读取数据 for(i=1; i<=sockMax; i++) { if(clientfd[i].fd < 0) continue; if (clientfd[i].revents & (POLLIN | POLLERR))//POLLRDNORM { if(recvLen != REVLEN) { while(1) { //recv数据 ret = recv(clientfd[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0); if(ret == 0) { clientfd[i].fd = -1; recvLen = 0; break; } else if(ret < 0) { clientfd[i].fd = -1; recvLen = 0; break; } //数据接受正常 recvLen = recvLen+ret; if(recvLen<REVLEN) { continue; } else { //数据接受完毕 printf("buf = %s\n", recvBuf); //close(client[i].fd); //client[i].fd = -1; recvLen = 0; break; } } } } } } return 0; }</span>
c). int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event );
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
epoll_create生成一个 Epoll 专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的 socket fd 上是否发生以及发生了什么事件。 size 就是你在这个 Epoll fd 上能关注的最大 socket fd 数,大小自定,只要内存足够。
epoll_ctl控制某个 Epoll 文件描述符上的事件:注册、修改、删除。其中参数 epfd 是 epoll_create() 创建 Epoll 专用的文件描述符。相对于 select 模型中的 FD_SET 和 FD_CLR 宏。op:EPOLL_CTL_ADD Register the target file descriptor fd on the epoll instance, EPOLL_CTL_MOD Change the event event associated with the target
file descriptor fd, EPOLL_CTL_DEL Remove (deregister) the target file descriptor fd from the epoll instance。
<span style="font-size:12px;">/* 实现功能:通过epoll, 处理多个socket * 监听一个端口,监听到有链接时,添加到epoll_event */ #include "select.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <poll.h> #include <sys/epoll.h> #include <sys/time.h> #include <netinet/in.h> typedef struct _CLIENT{ int fd; struct sockaddr_in addr; /* client's address information */ } CLIENT; #define MYPORT 59000 //最多处理的connect #define MAX_EVENTS 500 //当前的连接数 int currentClient = 0; //数据接受 buf #define REVLEN 10 char recvBuf[REVLEN]; //EPOLL相关 //epoll描述符 int epollfd; //事件数组 struct epoll_event eventList[MAX_EVENTS]; void AcceptConn(int srvfd); void RecvData(int fd); int main() { int i, ret, sinSize; int recvLen = 0; fd_set readfds, writefds; int sockListen, sockSvr, sockMax; int timeout; struct sockaddr_in server_addr; struct sockaddr_in client_addr; //socket if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket error\n"); return -1; } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(MYPORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //bind if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { printf("bind error\n"); return -1; } //listen if(listen(sockListen, 5) < 0) { printf("listen error\n"); return -1; } //1. epoll 初始化 epollfd = epoll_create(MAX_EVENTS); struct epoll_event event; event.events = EPOLLIN|EPOLLET; event.data.fd = sockListen; //2. epoll_ctrl if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockListen, &event) < 0) { printf("epoll add fail : fd = %d\n", sockListen); return -1; } //epoll while(1) { timeout=3000; //3. epoll_wait int ret = epoll_wait(epollfd, eventList, MAX_EVENTS, timeout); if(ret < 0) { printf("epoll error\n"); break; } else if(ret == 0) { printf("timeout ...\n"); continue; } //直接获取了事件数量,给出了活动的流,这里是和poll区别的关键 int n = 0; for(n=0; n<ret; n++) { //错误退出 if ((eventList .events & EPOLLERR) || (eventList .events & EPOLLHUP) || !(eventList .events & EPOLLIN)) { printf ( "epoll error\n"); close (eventList .data.fd); return -1; } if (eventList .data.fd == sockListen) { AcceptConn(sockListen); }else{ RecvData(eventList .data.fd); //不删除 // epoll_ctl(epollfd, EPOLL_CTL_DEL, pEvent->data.fd, pEvent); } } } close(epollfd); close(sockListen); printf("test\n"); return 0; } /************************************************** 函数名:AcceptConn 功能:接受客户端的链接 参数:srvfd:监听SOCKET ***************************************************/ void AcceptConn(int srvfd) { struct sockaddr_in sin; socklen_t len = sizeof(struct sockaddr_in); bzero(&sin, len); int confd = accept(srvfd, (struct sockaddr*)&sin, &len); if (confd < 0) { printf("bad accept\n"); return; }else { printf("Accept Connection: %d", confd); } //setnonblocking(confd); //4. epoll_wait //将新建立的连接添加到EPOLL的监听中 struct epoll_event event; event.data.fd = confd; event.events = EPOLLIN|EPOLLET; epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event); } //读取数据 void RecvData(int fd) { int ret; int recvLen = 0; memset(recvBuf, 0, REVLEN); printf("RecvData function\n"); if(recvLen != REVLEN) { while(1) { //recv数据 ret = recv(fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0); if(ret == 0) { recvLen = 0; break; } else if(ret < 0) { recvLen = 0; break; } //数据接受正常 recvLen = recvLen+ret; if(recvLen<REVLEN) { continue; } else { //数据接受完毕 printf("buf = %s\n", recvBuf); recvLen = 0; break; } } } printf("content is %s", recvBuf); }</span>
4. 网络协议分层模型:
5. 其他一些小问题:
a. 网络模型直接排序一般采用大端存储的。
b. keepalive???:
c. 长连接、短连接:
d. 防火墙如何利用TCP协议终止你的翻墙请求?
6. 网络分层模型和不同层的支持的协议:
(如何画时序图)
二、多线程编程:
1. 线程与进程:
线程共享:进程指令、大多数数据、打开的文件、信号处理函数和信号处置、当前工作目录、用户id和组ID
进程共享:线程ID、寄存器集合(程序计数器和函数指针)、栈、errno、信号掩码、优先级
线程&进程:线程是系统调度的最小单元,fork需要把父进程内存影响复制到子进程;fork返回以后父子进程需要进行IPC通信。
2. 多线程编程:
pthread_create( pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void arg);
pthread_join(pthread_t *tid, void **status) //如果status指针非空,把线程的返回值、一个指向某个对象的指针存入status指向的位置
pthread_self(void) // 线程自身id
pthread_detach(tid)// 线程终止时,所有资源被释放
pthread_exit(void *status)//线程终止
3. 多进程编程:
fork()//创建子进程
exec()//4个函数族。创建进程,但子进程执行时废弃当前进程的数据段和堆栈段
4. 进程/线程间同步\通信:
一般我们讨论进程间共享数据,线程间同步。因为线程很多数据都是共有的,而进程间很难共享数据,因此线程的同步和进程的数据共享一般都是我们面临的问题的。
a. 进程间共享/通信数据(IPC)方法:
管道、FIFO、共享内存、消息队列、信号、socket
对比:
1. 管道:速度慢、容量有限、只有父子进程通讯
2. FIFO: 任何进程都能通信,但速度慢
3. 消息队列:容量受限
4. 信号量:不能传递复杂信息
5. 共享内存:速度快、容量大。需要注意同步
1).
b. 线程间同步方法(SYNC):
临界区、锁、信号量、事件、interlocked variable
1). 锁:
pthread_mutex_t counter_mutex=PTHREAD_MUTEX_INITIALIZER; COND
pthread_mutex_lock(pthread_mutex_t * mptr);
// do something
pthread_mutex_unlock(pthread_mutex_t *mptr);
http://www.cnblogs.com/memewry/archive/2012/08/22/2651696.html
五、开源框架深入阅读和理解:
1. thrift协议的数据类型、协议、传输、服务类型
2. thrift协议源代码阅读
六、常用运行时程序排查:
1. 使用cp替换so文件为什么服务会core?
2. 如何排查内存泄露
七、分布式系统问题:
十、C++语言的新特性:1. 智能指针 std::shared_ptr 用法:
a). 智能指针是用来实现指针对象的共享和内存生存期自动管理(一般使用引用计数实现,我理解智能指针就是一个栈对象,在智能指针的生命期结束时,对智能指针指向的动态内存使用减1,如果计数减为0,则释放内存)。
b). 所有的智能指针都会重载* -> 等符号。
#include <boost/shared_ptr.hpp> using namespace std; class implementation { public: ~implementation() { std::cout <<"destroying implementation\n"; } void do_something() { std::cout << "did something\n"; } }; void test(){ boost::shared_ptr<implementation> sp1(new implementation()); boost::shared_ptr<implementation> sp2 = sp1; sp2.reset(); boost::shared_ptr<int> a1(ptr); std::cout<<*ptr<<endl; }
b). 使用智能指针注意事项:
a. 不要把原生指针给多个shared_ptr管理。
b. 不要在函数实参里创建shared_ptr。
c. shared_ptr作为被保护对象时小心循环引用。
d. 不要把this指针传给shared_ptr。
c). static_ptr_cast ???:
2. auto用法:
参考文献:
1. Unix网络编程:
2. thrift服务源代码分析: http://yanyiwu.com/work/2014/12/06/thrift-tnonblockingserver-analysis.html
3.
相关文章推荐
- c语言短信猫开发代码
- 1028 C语言文法
- C语言文法阅读与理解
- c语言文法分析
- c语言文法
- Effective C++笔记(一)
- 在c语言中自定义了一个函数,在main中调用时提示找不到标识符
- 如何禁止类的拷贝构造函数和复制构造函数
- c++11 线程同步信号的使用
- 非纯c++模式实现ogre1.9的Android版
- C语言文法
- 日志库EasyLogging++学习系列(7)—— 记录方式详解
- 如何解决C++中用eof()多读一次
- 在C#中使用c++编译的ocx的方法
- c++函数指针
- C++类成员指针
- C++类成员指针
- C++ const限定符之顶层const & 底层const & 指针 & 引用之间的关系
- C语言中内存分配
- C++设计模式——命令模式