僵尸进程过程

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. LA 3942 - Remember the Word 字典树+DP

    看题传送门:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show ...

  2. 【Codeforces Round #185 (Div. 2) A】 Whose sentence is it?

    [链接] 链接 [题意] 告诉你每句话; 然后每句话是谁说的和开头与结尾的一段字符串有关. [题解] 一个一个判断就好; 注意大小<5的情况 [错的次数] 在这里输入错的次数 [反思] 在这里输 ...

  3. js中退出语句break,continue和return 比较 (转)

    在 break,continue和return 三个关键字中, break,continue是一起的,return 是函数返回语句,但是返回的同时也将函数停止 首先:break和continue两个一 ...

  4. ZYNQ7000 LVDS接口输出配置

    xilinx 7系列芯片不再支持LVDS33电平,在VCCO电压为3.3V的情况下无法使用LVDS25接口. 有些设计者想通过在软件中配置为LVDS25,实际供电3.3V来实现LVDS33也是无效的, ...

  5. spark安装与调试

    I---- 1---jdk and scala install ****zyp@ubuntu:~/Desktop/software$ tar xvf jdk-7u67-linux-i586.tar.g ...

  6. 使用C#版本的gdal库打开hdf文件

    作者:朱金灿 来源:http://blog.csdn.net/clever101 最近应同事的请求帮忙研究下使用C#版的gdal库读取hdf文件,今天算是有一点成果,特地做一些记录. 首先是编译C#版 ...

  7. JavaScript 初学者应知的 24 条最佳实践

    原文:24 JavaScript Best Practices for Beginners (注:阅读原文的时候没有注意发布日期,觉得不错就翻译了,翻译到 JSON.parse 那一节觉得有点不对路才 ...

  8. 怎样用O2O去改变充满谎言、疑虑和愤慨的维修行业

    为什么千亿级的维修服务市场出不了行业巨头?   据相关统计,我国的整个维修服务市场规模可达每年数千亿元之巨(当中仅家电维修就可达近千亿规模,更遑论手机.数码.家具等维修). 相同是千亿级规模的服务行业 ...

  9. NavMesh动态碰撞

    今天遇到一个问题,就是怎样处理一些动态的障碍物. NavMesh是能够躲避静态的障碍物.NavMeshObstacle的作用就是动态添加障碍. 可是有个问题,NavMeshObstacle是圆,连椭圆 ...

  10. MySQL的表空间管理

    表空间: MySQL没有真正意义上的表空间管理. MySQL的Innodb包含两种表空间文件模式,默认的共享表空间和每个表分离的独立表空间. 一般来说,当数据量很小的时候建议使用共享表空间的管理方式. ...