Linux下的socket编程实践(四)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:

/**获取本机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种状态

放弃等于又一次可以选择的机会。

Linux下的socket编程实践(四)TCP服务端优化和常见函数

相关文章:

你感兴趣的文章:

标签云: