网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

18 | 防人之心不可无:检查数据的有效性

盛延敏 2019-09-11
你好,我是盛延敏,这里是网络编程实战第 18 讲,欢迎回来。
在前面一讲中,我们仔细分析了引起故障的原因,并且已经知道为了应对可能出现的各种故障,必须在程序中做好防御工作。
在这一讲里,我们继续前面的讨论,看一看为了增强程序的健壮性,我们还需要准备什么。

对端的异常状况

在前面的第 11 讲以及第 17 讲中,我们已经初步接触过一些防范对端异常的方法,比如,通过 read 等调用时,可以通过对 EOF 的判断,随时防范对方程序崩溃。
int nBytes = recv(connfd, buffer, sizeof(buffer), 0);
if (nBytes == -1) {
error(1, errno, "error read message");
} else if (nBytes == 0) {
error(1, 0, "client closed \n");
}
你可以看到这一个程序中的第 4 行,当调用 read 函数返回 0 字节时,实际上就是操作系统内核返回 EOF 的一种反映。如果是服务器端同时处理多个客户端连接,一般这里会调用 shutdown 关闭连接的这一端。
上一讲也讲到了,不是每种情况都可以通过读操作来感知异常,比如,服务器完全崩溃,或者网络中断的情况下,此时,如果是阻塞套接字,会一直阻塞在 read 等调用上,没有办法感知套接字的异常。
其实有几种办法来解决这个问题。
第一个办法是给套接字的 read 操作设置超时,如果超过了一段时间就认为连接已经不存在。具体的代码片段如下:
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv);
while (1) {
int nBytes = recv(connfd, buffer, sizeof(buffer), 0);
if (nBytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("read timeout\n");
onClientTimeout(connfd);
} else {
error(1, errno, "error read message");
}
} else if (nBytes == 0) {
error(1, 0, "client closed \n");
}
...
}
这个代码片段在第 4 行调用 setsockopt 函数,设置了套接字的读操作超时,超时时间为在第 1-3 行设置的 5 秒,当然在这里这个时间值是“拍脑袋”设置的,比较科学的设置方法是通过一定的统计之后得到一个比较合理的值。关键之处在读操作返回异常的第 9-11 行,根据出错信息是EAGAIN或者EWOULDBLOCK,判断出超时,转而调用onClientTimeout函数来进行处理。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(10)

  • LDxy
    1,最终缓冲区的大小应该比预计接收的数据大小大一些,预防缓冲区溢出。2,完全可以动态分配,但是要记得在return前释放缓冲区

    作者回复: 👍

    2019-09-12
    6
  • yusuf
    1、大小一般为2的多少次方
    2、不能换成动态分配。在read中需要sizeof指明接受数据的最大长度,malloc返回的是一个指针,求指针的sizeof时返回的是指针所占内存大小(32位为4,64位为8),跟实际数据的大小不一致
    2019-09-11
    2
    3
  • godtrue
    😅读完好像明白了,其实应该没明白,因为,课后思考题,答不上来!

    在网络编程中,是否做好了对各种异常边界的检测,将决定我们的程序在恶劣情况下的稳定性,所以,我们一定要时刻提醒自己做好应对各种复杂情况的准备,这里的异常情况包括缓冲区溢出、指针错误、连接超时检测等。
    2019-11-24
  • 西兹兹
    但仔细看一下,这段代码很有可能会产生下面的结果。 下面贴的是两句代码变量声明和赋值,并不是所谓的结果,求解

    作者回复: 是这样,声明了一个128字节的数组,但是实际使用的时候,对第129个元素进行了赋值操作,相当于访问了一个没有声明的内存地址,导致产生访问错误,程序会退出。

    2019-11-06
  • 西兹兹
    通过 recv 读取的字符数为 128 时,就会是文稿中的结果。 结果是什么?文稿里并没给出相关结果

    作者回复:
    char buffer[128];
    buffer[128] = '\0';

    文稿中给出了,变成缓冲区溢出了。

    2019-11-06
  • 传说中的成大大
    第一问:
    不能太小也不能太大 太小了频繁的用户态和内核态切换,太大了读不够容易阻塞,就算不阻塞也容易浪费
    第二问:
    如果用malloc频繁的申请和释放也不太好 容易造成碎片
    2019-09-15
  • 一周思进
    判断是否换行也可以直接strstr判断吧?
    https://mp.weixin.qq.com/s/YvfZMO2gCjHWmrNRGpdibA
    我觉得这两种方式的问题就是把后面读取的数据丢弃了,这对于tcp通信可能存在问题吧?
    在想后面是不是得换成全局循环缓冲区读写?

    作者回复: 嗯,是需要抽象一个buffer对象来做的。

    2019-09-13
  • 徐凯
    第一题。是不是跟结构体字节对齐一样的意思。数据比如是2的倍数 可以方便cpu处理?
    第二题 可以是动态内存 有时候应用层分包可以自定义几m一个包 甚至更大 而栈上分配空间是有限的 平均都在2到4m的样子 如果在栈上分配缓冲区 可能你的程序会根据平台不同选择性崩溃,而在堆上则没有这个问题唯一需要注意的是内存泄漏问题 c++有智能指针可以避免,java应该更方便吧 它的内存回收不是很厉害的嘛
    2019-09-12
  • 刘晓林
    老师,第二个例子中,及时加上了msg_length和缓冲区length的大小比较,如果msg_length写得很大(但小于length)而实际数据没有那么大时,服务器也会阻塞在read上吧?所以说判断msg_length<=length并不能接read阻塞的问题呀,只能解内存溢出的问题。

    作者回复: 是的,如果是这样,只能说我们双方的通信协议没有得到严格的遵守。

    2019-09-12
  • Steiner
    我觉得不能用动态分配,如果程序崩溃了,内存还没回收会内存溢出吧
    2019-09-11
    5
收起评论
10
返回
顶部