僵尸进程过程

1)通过忽略SIGCHLD信号,避免僵尸进程

在server端代码中加入

signal(SIGCHLD, SIG_IGN);

2)通过wait/waitpid方法。解决僵尸进程

signal(SIGCHLD,onSignalCatch);

void onSignalCatch(int signalNumber)
{
wait(NULL);
}

3) 假设多个客户端同一时候关闭, 问题描写叙述如以下两幅图所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvempmMjgwNDQxNTg5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

/** 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);
}

在客户执行过程中按下Ctrl+C,则能够看到在server端启动50个子进程,而且全部的客户端全部一起断开的情况下,产生的僵尸进程数是惊人的(此时也证明了SIGCHLD信号是不可靠的)!

解决方法-将server端信号捕捉函数改造例如以下:

void sigHandler(int signo)
{
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}

waitpid返回值解释:

on  success,  returns the process ID of the child whose state has changed(返回已经结束执行

的子进程的PID); if WNOHANG was specified and one or more child(ren) specified by pid exist,

but have not yet changed state, then 0 is returned(假设此时尚有好多被pid參数标识的子进程存在, 并

且没有结束的迹象, 返回0).  On error, -1 is returned.

地址查询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 */
/**获取本机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.例如以下图(客户端与服务器都在本机:两方(server的子进程,与client)链接已经建立(ESTABLISHED),等待通信)

2.最先close的一端,会进入TIME_WAIT状态; 而被动关闭的一端能够进入CLOSE_WAIT状态 (下图,server端首先关闭)

3.TIME_WAIT 时间是2MSL(报文的最长存活周期的2倍)

  原因:(ACK y+1)假设发送失败能够重发, 因此假设server端不设置地址反复利用的话, 服务器在短时间内就无法重新启动;

服务器端处于closed状态,不等于客户端也处于closed状态。

(下图, client先close, client出现TIME_WAIT状态)

4.TCP/IP协议的第1种状态:图上仅仅包括10种状态,另一种CLOSING状态

产生CLOSING状态的原因:

Server端与Client端同一时候关闭(同一时候调用close,此时两端同一时候给对端发送FIN包),将产生closing状态,最后两方都进入TIME_WAIT状态(例如以下图)。

SIGPIPE信号

往一个已经接收FIN的套接中写是同意的。接收到FIN仅仅代表对方不再发送数据;可是在收到RST段之后,假设还继续写,调用write就会产生SIGPIPE信号,对于这个信号的处理我们通常忽略就可以。

signal(SIGPIPE, SIG_IGN);

/** 測试: 在Client发送每条信息都发送两次
当Server端关闭之后Server端会发送一个FIN分节给Client端,
第一次消息发送之后, Server端会发送一个RST分节给Client端,
第二次消息发送(调用write)时, 会产生SIGPIPE信号;
注意: Client端測试代码使用的是下节将要介绍的Socket库
**/
void sigHandler(int signo)
{
if (SIGPIPE == signo)
{
cout << "receive SIGPIPE = " << SIGPIPE << endl;
exit(EXIT_FAILURE);
}
}
int main()
{
signal(SIGPIPE, sigHandler);
TCPClient client(8001, "127.0.0.1");
try
{
std::string msg;
while (getline(cin, msg))
{
client.send(msg);
client.send(msg); //第二次发送
msg.clear();
client.receive(msg);
client.receive(msg);
cout << msg << endl;
msg.clear();
}
}
catch (const SocketException &e)
{
cerr << e.what() << endl;
}
}

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

读写均关闭

1.close终止了数据传送的两个方向;

而shutdown能够有选择的终止某个方向的数据传送或者终止数据传送的两个方向。

2.shutdown how=SHUT_WR(关闭写端)能够保证对等方接收到一个EOF字符(FIN段),而无论是否有其它进程已经打开了套接字(shutdown并没採用引用计数)。

而close须要等待套接字引用计数减为0时才发送FIN段。也就是说直到全部的进程都关闭了该套接字。

演示样例分析:

客户端向服务器依照顺序发送:FIN E D C B A, 假设FIN是当client尚未接收到ABCDE之前就调用close发送的, 那么client端将永远接收不到ABCDE了, 而通过shutdown函数, 则能够有选择的仅仅关闭client的发送端而不关闭接收端, 则client端还能够接收到ABCDE的信息;

/**測试: 实现与上面相似的代码(使用close/shutdown)两种方式实现 **/

完整源码请參照:

http://download.csdn.net/detail/hanqing280441589/8486517

注意: 最好读者须要有select的基础, 没有select基础的读者能够參考我的博客<Socket编程实践(8)>相关章节

版权声明:本文博客原创文章,博客,未经同意,不得转载。

最新文章

  1. bootstrap和bootstrap-select的outline设置
  2. [产品相关] A/B测试终极指南(翻译)
  3. 能够将 HTML 表格转换成图表的jQuery插件:Chartinator
  4. eclipse配置hadoop的错误
  5. 对plist文件的简单封装
  6. zoj 3708 Density of Power Network
  7. 蛋疼的Apple IOS Push通知协议
  8. 【夯实基础】javakeywordsynchronized 详细说明
  9. ASP.NET MVC5+EF6+EasyUI 后台管理系统(84)-Quartz 作业调度用法详解一
  10. Excel 2010高级应用-圆环图(七)
  11. Luogu P5290 [十二省联考2019]春节十二响
  12. 【一本通1329:【例8.2】细胞&amp;&amp;洛谷P1451 求细胞数量】
  13. Java对象、引用、实例
  14. 深入MySQL复制(二):基于GTID复制
  15. VS2013 VS2015 VS2017调试出现无法启动iis express web服务器
  16. Linux lvm 分区知识笔记
  17. Excel列名和列序号转换
  18. Windows常用命令实例
  19. 如何使用JDBC查询所有记录
  20. PHP 使用GD 库绘制图像,无法显示的问题

热门文章

  1. Bootstrap相关优质项目学习清单
  2. FireBreath与JS交互1
  3. C语言数组初始化的问题
  4. Perl自动释放Licence启动Verdi
  5. 异步FIFO总结
  6. 微信小程序初步运营方案
  7. thinkphp中ajax使用实例(thinkphp内置支持ajax)
  8. JavaMail| JavaMail配置属性
  9. ajax跳转到新的jsp页面(局部刷新)
  10. [Typescript] Sorting arrays in TypeScript