server 编程
来源:互联网 发布:女王网络聊天记录 编辑:程序博客网 时间:2024/05/22 12:29
1.signal(SIGPIPE,SIG_IGN) 忽略SIGPIPE信号
如果客户端关闭套接字,而服务器调用了一次write,服务器会接收一个RST segment(Tcp传输层)如果服务器再次调用了write,这个时候会产生SIGPIPE信号。linux默认的处理方式是退出进程。
2.TIME_WAIT 状态 对大并发服务器的影响
应尽可能在服务器避免出现 TIME_WAIT状态.
如果服务器端 主动断开连接(先于client调用close),服务端就会进入TIME_WAIT协议设计上,应该让客户端主动断开连接,这样就把TIME_WAIT状态分散到大量的客户端。如果客户端不活跃了,一些客户端不断开连接,这样子就会占用服务器的连接资源。服务器需有个机制来踢掉不活跃的连接。
3.signeal(SIGCHLD,SIG_IGN)
子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。事实上,由于UNIX的历史原因,要想不产生僵尸进程还有另外一种办法:父进程用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。系统默认的忽略动作和用户sigaction函数自定义的忽略通常是没有区别的,但这是一个特例。此方法对于Linux可用,但不保证在其它UNIX系统上都可用。请编写程序验证这样做不会产生僵尸进程。
4.socket(PF_INET,SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC,IPPROTO_TCP)
大部分这种问题都能够解决,在文章的最后,提到了一种特殊情况,就是父子进程中的端口占用情况。父进程监听一个端口后,fork出一个子进程,然后kill掉父进程,再重启父进程,这个时候提示端口占用,用netstat查看,子进程占用了父进程监听的端口。
原理其实很简单,子进程在fork出来的时候,使用了写时复制(COW,Copy-On-Write)方式获得父进程的数据空间、 堆和栈副本,这其中也包括文件描述符。刚刚fork成功时,父子进程中相同的文件描述符指向系统文件表中的同一项(这也意味着他们共享同一文件偏移量)。这其中当然也包含父进程创建的socket。
接着,一般我们会调用exec执行另一个程序,此时会用全新的程序替换子进程的正文,数据,堆和栈等。此时保存文件描述符的变量当然也不存在了,我们就无法关闭无用的文件描述符了。所以通常我们会fork子进程后在子进程中直接执行close关掉无用的文件描述符,然后再执行exec。但是在复杂系统中,有时我们fork子进程时已经不知道开了多少个文件描述符(包括socket句柄等),这此时进行逐一清理确实有很大难度。我们期望的是能在fork子进程前打开某个文件句柄时就指定好:“这个句柄我在fork子进程后执行exec时就关闭”。其实时有这样的方法的:即所谓 的 close-on-exec。回到我们的应用场景中来,只要我们在创建socket的时候加上 SOCK_CLOEXEC标志,就能够达到我们要求的效果,在fork子进程中执行exec的时候,会清理掉父进程创建的socket。
当然,其他的文件描述符也有类似的功能,例如文件,可以在打开的时候使用O_CLOEXEC标识( linux 2.6.23才开始支持此标记 ),达到和上面一样的效果。或者使用系统的fcntl函数设置FD_CLOEXEC即可。
5.connfd = accept4(listenfd, (struct sockaddr*)&peeraddr,&peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
accept4可以给接收的套接字设置标志
6.EMFILE
当套接字资源使用完,后accept将进入忙等待状态,一直处于活跃。此时可采用方法:
1.创建一个无用套接字
int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
2.当出现EMFILE时,将无用套接字关闭。再接收新连接,后立即关闭。最后重新建立无用套接字
if (connfd == -1){if (errno == EMFILE){close(idlefd);idlefd = accept(listenfd, NULL, NULL);close(idlefd);idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);continue;}elseERR_EXIT("accept4");}
7.写缓冲
当写缓冲满时,可以监听其OUT事件。直到数据发送完,再取消关注OUT(可写)时间
8.读缓冲
socket_ret = read(m_hostClientFd, buf, 1500);if (socket_ret > 0){ std::string tstr(buf, socket_ret); m_msgbuf.append(tstr); while (m_msgbuf.size() > 0) { // we have already some data in the message buffer std::string::size_type len = m_msgbuf.find_first_of('\004'); if (std::string::npos != len) { // Complete message in the buffer std::cout<<"--read complete message--"<<std::endl; //process(m_msgbuf.substr(0, len));//不包括 '\004' m_msgbuf.erase(0, len+1); std::cout<<"--process message complete--"<<std::endl; } else { break; } }}
9.epool的 边沿触发与电平触发
边沿触发:缓存区非满:高电平; 满:低电平 当缓冲区数据满时,电平有高到低,触发EPOOLOUT事件当缓冲区数据由无到有,电平由低到高,触发EPOOLIN时间所以读数据时,需读到其返回EAGAIN错误,此时缓冲区为空;否则,当有数据时其不会出现边沿触发,一直处于高电平;同理写数据时,也需返回EAGAIN错误。解决EMFILE,存在问题。虽然不会出现busy-loop,但后续套接字也无法处理。因为一直处于高电平。
电平触发:
缓冲区为空 低电平
缓冲区不为空 高电平 触发EPOOLIN事件
10.是不是使用epool效率就高呢?
因为epool内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epool没有select及pool两者的线性下降的性能问题;
但是如果已连接套接字数量不多,且套接字非常活跃。此时epool在内核态会不断调用fd的callback,其相对select及poll效率可能更低。
- server 编程
- SQL SERVER 编程经验谈
- web server编程学习
- linux socket编程-server
- Java Socket Server编程
- sql server编程语法
- UDP编程·server
- TCP-server编程
- JAVA 网络编程 Socket server client 编程
- 编程创建 SQL Server 数据库
- SQL Server编程经验技巧
- 编程管理sql server帐号
- 常用SQL SERVER 编程 技巧
- symbian client server 编程汇总
- SQL Server 数据库基础编程
- SQL Server Transact-SQL 编程
- 网络编程client和server
- QT Socket编程--UDP Server
- 树型dp poj3071 Football
- 应用程序的版本升级
- Android开发书籍推荐
- 反向传播BP算法
- Android材料设计兼容函数库(Design Support Library)(I)导航视图(Navigation View)
- server 编程
- android 显示特殊符号
- 学习心得
- Verilog中parameter和define的区别
- 新建一个安卓工程
- Myeclipse配置Tomcat
- hdu5313Bipartite Graph(二分图染色+DP(bitset优化))
- 011-Scala中的apply实战详解
- 画函数图形的C#程序,兼论一个病态函数