标签:linux 优化 shutdown close tcp
并发下的僵尸进程处理
只有一个进程连接的时候,我们可以使用以下两种方法处理僵尸进程:
1)通过忽略SIGCHLD信号,避免僵尸进程
在server端代码中添加
signal(SIGCHLD, SIG_IGN);
2)通过wait/waitpid方法,解决僵尸进程
signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); }那么如果是多进程状态下多个客户端同时关闭呢?
我们可以用下面的客户端代码测试:
/** client端实现的测试代码**/ int main() { int sockfd[50]; for (int i = 0; i < 50; ++i) { if ((sockfd[i] = socket(AF_INET, SOCK_STREAM, 0)) == -1) err_exit("socket error"); struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8001); serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sockfd[i], (const struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) err_exit("connect error"); } sleep(20); }此时由于信号的同时到达,并且SIGCHLD又是不可靠信号,不支持排队,会留下相当部分的僵尸进程
解决方法:
使用循环的 waitpid函数就可以将所有的子进程留下的僵尸进程处理掉
void sigHandler(int signo) { while (waitpid(-1, NULL, WNOHANG) > 0) ; } //pid=-1 等待任何子进程,相当于 wait()。 //WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。地址查询的API
#include <sys/socket.h> int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //获取本地addr结构 int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //获取对方addr结构 int gethostname(char *name, size_t len); int sethostname(const char *name, size_t len); #include <netdb.h> extern int h_errno; struct hostent *gethostbyname(const char *name); #include <sys/socket.h> /* for AF_INET */ struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type); struct hostent *gethostent(void);
//hostent结构体 struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses */ } #define h_addr h_addr_list[0] /* for backward compatibility */
这两个函数调用的时机很重要,否则不能得到正确的地址和端口:
TCP
对于服务器来说,在bind以后就可以调用getsockname来获取本地地址和端口,虽然这没有什么太多的意义。getpeername只有在链接建立以后才调用,否则不能正确获得对方地址和端口,所以他的参数描述字一般是链接描述字而非监听套接口描述字。
对于客户端来说,在调用socket时候内核还不会分配IP和端口,此时调用getsockname不会获得正确的端口和地址(当然链接没建立更不可能调用getpeername),当然如果调用了bind 以后可以使用getsockname。想要正确的到对方地址(一般客户端不需要这个功能),则必须在链接建立以后,同样链接建立以后,此时客户端地址和端口就已经被指定,此时是调用getsockname的时机。
UDP
UDP分为链接和没有链接2种(这个到UDP与connect可以找到相关内容)
没有链接的UDP不能调用getpeername,但是可以调用getsockname,和TCP一样,他的地址和端口不是在调用socket就指定了,而是在第一次调用sendto函数以后
已经链接的UDP,在调用connect以后,这2个函数都是可以用的(同样,getpeername也没太大意义。如果你不知道对方的地址和端口,不可能会调用connect)。
/**获取本机IP列表**/ int gethostip(char *ip) { struct hostent *hp = gethostent(); if (hp == NULL) return -1; strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr)); return 0; } int main() { char host[128] = {0}; if (gethostname(host, sizeof(host)) == -1) err_exit("gethostname error"); cout << "host-name: " << host << endl; struct hostent *hp = gethostbyname(host); if (hp == NULL) err_exit("gethostbyname error"); cout << "ip list: " << endl; for (int i = 0; hp->h_addr_list[i] != NULL; ++i) { cout << '\t' << inet_ntoa(*(struct in_addr*)hp->h_addr_list[i]) << endl; } char ip[33] = {0}; gethostip(ip); cout << "local-ip: " << ip << endl; }简述TCP 11种状态
1.客户端和服务器连接建立的时候,双方处于ESTABLISHED(建立)状态
2.关于TIME_WAIT状态 详见 http://www.mamicode.com/info-detail-190400.html
3.TCP/IP协议的第11种状态:图上只包含10种状态,还有一种CLOSING状态
产生CLOSING状态的原因:
Server端与Client端同时关闭(同时调用close,此时两端同时给对端发送FIN包),将产生closing状态,最后双方都进入TIME_WAIT状态。(因为主动关闭的一方会进入TIME_WAIT状态,双方同时主动关闭,则都进入)
往一个已经接收FIN的套接中写是允许的,接收到FIN仅仅代表对方不再发送数据;但是在收到RST段之后,如果还继续写,调用write就会产生SIGPIPE信号,对于这个信号的处理我们通常忽略即可。
signal(SIGPIPE, SIG_IGN);
SIGPIPE,虽然已经接受到FIN,但是我还可以发送数据给对方;如果对方已经不存在了,那么TCP会进行重置,TCP协议栈发送RST段,收到RST后,再进行write会产生SIGPIPE信号。
其实很好理解:TCP可以看作是一个全双工的管道,读端信号不存在了,如果再对管道进行写的话会导致SIGIPPE信号的产生,处理的时候是忽略这个信号就可以了,其实就是按照管道的规则。
我们测试的时候在Client发送每条信息都发送两次,当Server端关闭之后Server端会发送一个FIN分节给Client端, 第一次消息发送之后, Server端会发送一个RST分节给Client端;第二次消息发送(调用write)时, 会产生SIGPIPE信号。
close和shutdown函数的区别
#include <unistd.h> int close(int fd); #include <sys/socket.h> int shutdown(int sockfd, int how);
shutdown的how参数 |
|
SHUT_RD |
关闭读端 |
SHUT_WR |
关闭写端 |
SHUT_RDWR |
读写均关闭 |
shutdowm how=1就可以保证对等方接收到一个EOF字符,而不管其他进程是否已经打开可套接字。
而close不能保证,直到套接字引用计数减为0的时候才发送。也就是说知道所有进程都关闭了套接字。
调用close函数,可能导致全双工的管道还没有回射给客户端时,产生丢失数据现象;例如
FIN D C B A
A B C D ----》丢失,已经关闭close
Close准确的含义是 套接字引用计数减为0 的时候,才发送FIN
int conn; conn=accept(sock,NULL,NULL); pid_t pid=fork(); if(pid==-1) ERR_EXIT("fork"); if(pid==0) { close(sock); //通信 close(conn); //这时才会向对方发送FIN段(因为这个时候conn引用计数减为0) } else if(pid>0) close (conn); //不会向客户端发送FIN段,仅仅只是将套接字的引用计数减1
版权声明:本文为博主原创文章,未经博主允许不得转载。
Linux下的socket编程实践(四)TCP服务端优化和常见函数
标签:linux 优化 shutdown close tcp
原文地址:http://blog.csdn.net/nk_test/article/details/48900807