僵尸进程过程

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)>相关章节

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

Socket编程实践(6) --TCPNotes服务器的更多相关文章

  1. C# socket编程实践

    C# socket编程实践——支持广播的简单socket服务器   在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...

  2. Socket编程实践(10) --select的限制与poll的使用

    select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...

  3. Socket编程实践(6) --TCP服务端注意事项

    僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...

  4. 5.1 socket编程、简单并发服务器

    什么是socket? socket可以看成是用户进程与内核网络协议栈的编程接口.是一套api函数. socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机间的进程间通信. 工业上使用的为t ...

  5. C# socket编程实践——支持广播的简单socket服务器

    在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...

  6. Socket编程实践(2) Socket API 与 简单例程

    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...

  7. socket编程:客户端与服务器间的连接以及各函数的用法

    在认真的看UNP之前,一直被socket编程说的云里雾里,今天我要让大家从整天上认识socket编程,让我们知道socket编程的整个流程和各个函数的用法.这样:我们在写一些简单的socket编程时就 ...

  8. Socket编程实践(1) 基本概念

    1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...

  9. Socket编程实践(3) 多连接服务器实现与简单P2P聊天程序例程

    SO_REUSEADDR选项 在上一篇文章的最后我们贴出了一个简单的C/S通信的例程.在该例程序中,使用"Ctrl+c"结束通信后,服务器是无法立即重启的,如果尝试重启服务器,将被 ...

随机推荐

  1. [TypeStyle] Add responsive styles using TypeStyle Media Queries

    Media queries are very important for designs that you want to work on both mobile and desktop browse ...

  2. sublime课程3 emmet插件中的常用符号有哪些

    sublime课程3 emmet插件中的常用符号有哪些 一.总结 一句话总结:emmet插件中的符号和css选择器里面哪些符号的意思很像. 1.+是干嘛的? 组合 2.{}是干嘛的? 标签里面的inn ...

  3. css3-10 如何使用滚动条

    css3-10 如何使用滚动条 一.总结 一句话总结:给设置了宽高的块标签使用,直接将overflow属性写到style里面即可. 1.滚动条的使用对象时谁? 一般是div,当div比较小(设置了宽高 ...

  4. MHA 一主两从搭建-脚本VIP-自动切换

    环境介绍:主机名 IP MHA角色 MySQL角色node1 192.168.56.26 Node MySQL Master node2 192.168.56.27 Node MySQL Master ...

  5. 从源码角度实现一个自己的Promise

    原文链接:https://geniuspeng.github.io/2017/12/14/my-promise/ 关于Promise的概念以及意义就不在这里介绍了,最近看到了一些实现Promise的核 ...

  6. 域名从www跳转到非www,Apache和Nginx2种解决方式

     背景:www跳转到非www. http://www.jiutianniao.com和http://jiutianniao.com 都可以访问. 但是,想把www这个重定向到非www,输入更简单,让搜 ...

  7. Android图文具体解释属性动画

    Android中的动画分为视图动画(View Animation).属性动画(Property Animation)以及Drawable动画.从Android 3.0(API Level 11)開始. ...

  8. svn删除文件出错的经验总结

    作者:朱金灿 来源:http://blog.csdn.net/clever101 今天有个同事在没有将工程从VS解决方案中移除的情况下使用svn对一个工程重命名,结果在提交时出错了,提示:没有匹配的可 ...

  9. SpringMVC拦截器-路径语法-略坑

    项目中遇到一种场景,登录拦截器需要拦截.html后缀等动态请求,但是发现语法不对头.    <mvc:interceptors>      <mvc:interceptor> ...

  10. Erlang与ActionScript3采用JSON格式进行Socket通讯

    http://hideto.iteye.com/blog/235811 需要下载as3corelib来为ActionScript3处理JSON codec server.erl -module(ser ...