tcpser

void
str_echo(int sockfd)
{
long arg1, arg2;
ssize_t n;
char line[MAXLINE]; for ( ; ; ) {
if ( (n = Readline(sockfd, line, MAXLINE)) == 0)        // 当读到EOF时(即对端close),函数返回,
return; /* connection closed by other end */ if (sscanf(line, "%ld%ld", &arg1, &arg2) == 2)
snprintf(line, sizeof(line), "%ld\n", arg1 + arg2);
else
snprintf(line, sizeof(line), "input error\n"); n = strlen(line);
Writen(sockfd, line, n);
}
} int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); for ( ; ; ) {
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen); if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}

tcpcli

void
str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE]; while (Fgets(sendline, MAXLINE, fp) != NULL) {  // 行输入,当键入 <C-D>即输入EOF时,返回NULL,函数返回 Writen(sockfd, sendline, strlen(sendline)); if (Readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely"); Fputs(recvline, stdout);
}
} int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr; if (argc != 2)
err_quit("usage: tcpcli <IPaddress>"); sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr); Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); str_cli(stdin, sockfd); /* do it all */ exit(0);
}

以上面程序为基础,进行修改

1. 多进程服务器,对僵死进程的处理

void
sig_chld(int signo)
{
pid_t pid;
int stat; while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) {
printf("child %d terminated\n", pid);
}
return;
}

Signal(SIGCHLD, sig_chld);

捕捉处理SIGCHLD,sig_child 中使用 waitpid 非阻塞方式,理由是:

  虽然 信号处理函数执行时,SIGCHILD被阻塞,但是SIGCHILD是不排队的,即若有多个SIGCHILD发出,则 sig_child 返回后,只会再被调用一次。

  所以使用 waitpid 非阻塞方式,在一次sig_child调用时处理完所有剩余的 僵死进程。

1.2 信号处理的副作用

  信号处理会打断慢系统调用。解决方法有:

    (1)设置 sigaction 设置 flags 时,添加 SA_RESTART,交给内核重启。

    (2)处理慢系统调用 EINTR。

  后者更推荐,因为不是所有系统调用都支持 SA_RESTART 重启。

2. accept 返回前,连接中止

场景:

  服务器过于繁忙,来不及处理已建立连接的套接字。

  客户端等待太久,向服务端发了RST

  服务端应用层调用accpet,报错,返回 ECONNABORTED ,服务器可以忽略该报错,重新执行accept

3. 服务器进程终止

  启动服务器/客户端,连接后,杀死服务器子进程。

  子进程收到kill信号,进程退出,并关闭所有打开的文件描述符,服务端发送FIN。

  客户端TCP服务接受FIN,向套接字接受缓冲区写入EOF,并进入CLOSE_WAIT状态,而应用程序阻塞在fgets。

  客户端键入值后,会继续 write 给服务器端(TCP服务认为此时处于半关闭)

  服务端对应进程已经关闭,所以TCP服务收到数据后,会返回RST。

  客户端read获得EOF,知道对端已经关闭。

  本例子的问题:

    服务端进程关闭后,发送的FIN不能及时通知给客户端进程, 原因是:

    客户端在其他文件描述符处阻塞了。

    可以使用 select poll 解决。

3.1 固执的向已关闭的对端写数据

  当客户端的TCP服务接受到RST后,知道了对端已经关闭。

  客户端的应用程序虽然收到了EOF,但不理会,并向对端写更多数据,

  内核会对,向收到RST套接字执行写操作的进程,发送SIGPIPE信号。

  客户端应用程序收到SIGPIPE,默认动作是exit,并且write会返回EPIPE错误。

  

  通常 对于 SIGPIPE 选择 SIG_IGN

4. 服务器崩溃

  服务器/客户端正常连接后,直接断开服务器的网线,并重启

  由于服务器崩溃,所以未能发出FIN

  客户端应用执行 write,将数据交给TCP,后执行readline

  客户端TCP会重传数据,并期待对方ACK,大约9分钟后,向应用层报错。

  客户端应用由于阻塞在read ,所以在read处获得TCP报错, ETIMEOUT 或 EHOSTUNREACH 或ENETUNREACH

  我们希望客户端能更快的检查出对端已崩溃,可以选择

    (1)readline 设置超时

    (2)SO_KEEPALIVE 套接字选项

    (3)客户端心跳

4.1 服务崩溃后重启

  客户端正在不停的重发报,并期待ACK

  服务端重启完成。

  服务端TCP会返回RST

  由于客户端应用程序阻塞在readline,所以返回ECONNRESET错误

5. 服务器关机

  客户端/服务器正常通信后

  服务器关机,init进程向所有进程发送 SIGTERM,几秒后再发送SIGKILL,所以服务端会关闭所有套接字,TCP发送FIN。

  客户端应用会阻塞在readline,而不能即使知道服务端已经死掉。

  使用 select , poll 能解决这问题

6.地址和fd

  有时只知道fd,不知道地址对,可以使用 getsockname 和 getpeername 获得地址对

7.数据格式

  通信时,如果通信双方需要以某种格式理解接受的数据,而不是向上面的程序一样,单纯的收发。

  则需要注意数据格式。

  比如,A,B的大小端不同,A给B发数据,A是大端,发送 0x0102 的二进制串,B接受后虽然内存中二进制串的排列没变,但是理解方式是安装小端,于是A,B通信出错。

  另外系统位数也会造成问题。

  所以,通常选择合适的编码方式进行发送,比如文本方式,而非二进制方式。

  或显示定义 二进制格式(位数,大小端)

UNP——第五章,TCP客户/服务程序的更多相关文章

  1. UNP学习笔记(第五章 TCP客户/服务程序实例)

    我们将在本章使用前一章中介绍的基本函数编写一个完整的TCP客户/服务器程序实例 这个简单得例子是执行如下步骤的一个回射服务器: TCP回射服务器程序 #include "unp.h" ...

  2. 第四章 基本TCP套接字编程 第五章 TCP客户/服务器程序实例

    TCP客户与服务器进程之间发生的重大事件时间表 TCP服务器 socket() --- bind() --- listen() --- accept() --- read() --- write -- ...

  3. UNIX网络编程 第5章 TCP客户/服务器程序示例

    UNIX网络编程 第5章 TCP客户/服务器程序示例

  4. UNP学习笔记(第十五章 UNIX域协议)

    UNIX域协议是在单个主机上执行客户/服务器通信的一种方法 使用UNIX域套接字有以下3个理由: 1.UNIX域套接字往往比通信两端位于同一个主机的TCP套接字快出一倍 2.UNIX域套接字可用于在同 ...

  5. UNIX网络编程---TCP客户/服务器程序示例(五)

    一.概述 客户从标准输入读入一行文本,并写给服务器 服务器从网络输入读入这行文本,并回射给客户 客户从网络输入读入这行回射文本,并显示在标准输出上 二.TCP回射服务器程序:main函数 这里给了函数 ...

  6. UNP学习第五章

    一.概述 想要写一个完整的TCP客户-服务器程序例子,有下面功能的回射服务器 1.客户从标准输入读一行文本,写到服务器上: 2.服务器从网络输入读此行,并回射给客户: 3.客户读此回射行并写到标准输出 ...

  7. UNP学习笔记2——从一个简单的ECHO程序分析TCP客户/服务器之间的通信

    1 概述 编写一个简单的ECHO(回复)程序来分析TCP客户和服务器之间的通信流程,要求如下: 客户从标准输入读入一行文本,并发送给服务器 服务器从网络输入读取这个文本,并回复给客户 客户从网络输入读 ...

  8. UNIX 网络编程第五章读书笔记

    刚看完 UNIX 第五章内容,我想按照自己的方式将自己获得的知识梳理一遍,以便日后查看!先贴上一段简单的 TCP 服务器端代码: #include <sys/socket.h> #incl ...

  9. 2019寒假训练营第三次作业part1-网络空间安全概论第五章

    第五章 网络攻防技术 5.1 网路信息收集技术--网络踩点 黑客入侵系统之前,需要了解目标系统可能存在的: 管理上的安全缺陷和漏洞 网络协议安全缺陷与漏洞 系统安全缺陷与漏洞 黑客实施入侵过程中,需要 ...

随机推荐

  1. 如何轻松使用 C 语言实现一个栈?​

    什么是数据结构? 数据结构是什么?要了解数据结构,我们要先明白数据和结构,数据就是一些int char 这样的变量,这些就是数据,如果你是一个篮球爱好者,那么你的球鞋就是你的数据,结构就是怎么把这些数 ...

  2. swoole热启动

    通过扫描指定的要扫描的目录,把所有文件找出来,分别md5 连接字符串,最后再md5返回 启动定时器,扫描,当前的加密值和以前一样不管,否则就重启服务,把当前赋值给旧值 . httpServer.php ...

  3. kinaba 安装踩坑: FATAL Error: [elasticsearch.url]: definition for this key is missing 转

     安装  https://www.jianshu.com/p/875457cb8da6   操作系统:Linux kibana 版本: 7.4.0 1. 在/etc/yum.repos.d/ 下新建 ...

  4. CentOS8平台nginx日志的定时切分

    一,编写bash脚本: [root@yjweb crontab]# vi split_nginx_logs.sh 代码: #!/bin/bash # 备份nginx的日志 # 昨天的日期 file_d ...

  5. ImageMagick:用identify检查图片是否完整?(jpg/gif/png图片是否损坏)

    一,常用图片格式的结束标志是什么? 1,Jpg格式的文件在16进制中的表示是以 ff d9 两个字节结尾 2,  gif格式的文件,结尾是 3b 3,  png格式的文件,结尾是  00 00 00 ...

  6. django—视图相关

    FBV与CBV FBV:function based view   基于函数的视图 CBV:class based view  基于类的视图 CBV的定义: from django.views imp ...

  7. D. Yet Another Problem On a Subsequence 解析(DP)

    Codeforce 1000 D. Yet Another Problem On a Subsequence 解析(DP) 今天我們來看看CF1000D 題目連結 題目 略,請直接看原題 前言 這題提 ...

  8. JQuery如何实现统计图表

    EEP JQuery如何实现统计图表 讯光科技 前言 在ERP项目开发过程中,统计图表(chart)普遍应用于各种统计和报表中,其形象直观,内容清晰.EEP的JQuery网站项目使用了Easyui 插 ...

  9. SQL Server 列存储索引 第三篇:维护

    列存储索引分为两种类型:聚集的列存储索引和非聚集的列存储索引,在一个表上只能创建一个聚集索引,要么是聚集的列存储索引,要么是聚集的行存储索引,然而一个表上可以创建多个非聚集索引. 一,创建列存储索引 ...

  10. 我叫MongoDb,不懂我的看完我的故事您就入门啦!

    这是mongo基础篇,后续会连续更新4篇 大家好我叫MongoDb,自从07年10月10gen团队把我带到这个世界来,我已经13岁多啦,现在越来越多的小伙伴在拥抱我,我很高兴.我是NoSQL大家族的一 ...