网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
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讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?

盛延敏 2019-10-28
你好,我是盛延敏,这里是网络编程实战的第 35 讲,欢迎回来。
这一篇文章是实战篇的答疑部分,也是本系列的最后一篇文章。非常感谢你的积极评论与留言,让每一篇文章的留言区都成为学习互动的好地方。在今天的内容里,我将针对评论区的问题做一次集中回答,希望能帮助你解决前面碰到的一些问题。
有关这部分内容,我将采用 Q&A 的形式来展开。

为什么在发送数据时,会先尝试通过 socket 直接发送,再由框架接管呢?

这个问题具体描述是下面这样的。
当应用程序需要发送数据时,比如下面这段,在完成数据读取和回应的编码之后,会调用 tcp_connection_send_buffer 方法发送数据。
//数据读到buffer之后的callback
int onMessage(struct buffer *input, struct tcp_connection *tcpConnection) {
printf("get message from tcp connection %s\n", tcpConnection->name);
printf("%s", input->data);
struct buffer *output = buffer_new();
int size = buffer_readable_size(input);
for (int i = 0; i < size; i++) {
buffer_append_char(output, rot13_char(buffer_read_char(input)));
}
tcp_connection_send_buffer(tcpConnection, output);
return 0;
}
而 tcp_connection_send_buffer 方法则会调用 tcp_connection_send_data 来发送数据:
int tcp_connection_send_buffer(struct tcp_connection *tcpConnection, struct buffer *buffer) {
int size = buffer_readable_size(buffer);
int result = tcp_connection_send_data(tcpConnection, buffer->data + buffer->readIndex, size);
buffer->readIndex += size;
return result;
}
在 tcp_connection_send_data 中,如果发现当前 channel 没有注册 WRITE 事件,并且当前 tcp_connection 对应的发送缓冲无数据需要发送,就直接调用 write 函数将数据发送出去。
//应用层调用入口
int tcp_connection_send_data(struct tcp_connection *tcpConnection, void *data, int size) {
size_t nwrited = 0;
size_t nleft = size;
int fault = 0;
struct channel *channel = tcpConnection->channel;
struct buffer *output_buffer = tcpConnection->output_buffer;
//先往套接字尝试发送数据
if (!channel_write_event_is_enabled(channel) && buffer_readable_size(output_buffer) == 0) {
nwrited = write(channel->fd, data, size);
if (nwrited >= 0) {
nleft = nleft - nwrited;
} else {
nwrited = 0;
if (errno != EWOULDBLOCK) {
if (errno == EPIPE || errno == ECONNRESET) {
fault = 1;
}
}
}
}
if (!fault && nleft > 0) {
//拷贝到Buffer中,Buffer的数据由框架接管
buffer_append(output_buffer, data + nwrited, nleft);
if (!channel_write_event_is_enabled(channel)) {
channel_write_event_enable(channel);
}
}
return nwrited;
}
这里有同学不是很理解,为啥不能做成无论有没有 WRITE 事件都统一往发送缓冲区写,再把 WRITE 事件注册到 event_loop 中呢?
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(8)

  • 龙骑士
    看了代码,好像connection对象没有释放,不知道是不是看漏了

    作者回复: 我仔细看了下,确实在handle_connection_closed方法中需要增加释放connection对象,感谢指正,方便的话,不知道是否可以提一个PR修复一下。

    2019-12-01
  • 小蚂蚁
    为什么在发送数据时,会先尝试通过 socket 直接发送,再由框架接管呢?
    老师你好,这个问题中,发送缓冲区有数据说明发送效率低(数据多,网络差等原因导致),没有注册WRITE事件是什么意思呢?(感觉这时一个基础问题[小尴尬])

    作者回复: 有两种发送数据的方式,第一种是通过注册WRITE事件,等待reactor来驱动我们把数据发送出去;第二种是不需要reactor驱动,直接往套接字上发送。这里的解释是说,在大部分情况下,为了效率,直接往套接字上发送,当一次解决不了时,再通过reactor来驱动数据发送。

    2019-11-30
  • yusuf
    // add event read for the new connection
    struct channel *channel1 = channel_new(connected_fd, EVENT_READ, handle_read, handle_write, tcpConnection);

    请问这里第4个参数设置了handle_write函数,为什么第2个参数没有设置EVENT_WRITE呢?
    原本以为这个地方是漏掉了EVENT_WRITE,可添加上EVENT_WRITE后,发现tcp服务器收到数据后会一直打印,而http服务器响应一次请求后会崩溃。这又是为什么呢?

    作者回复: 这里是向reactor注册了数据可读的事件,注意这个时候缓冲区是没有写入的需求的,如果注册了可写事件,相当于这个事件是肯定会发生的(因为套接字写缓冲区都是空的,可以往里写),所以这个时候你会看到一直会打印。

    也就是说,只有在真正有数据需要发送的时候,才需要注册EVENT_WRITE,让reator驱动把需要发送的数据发送完。

    2019-11-26
  • Ray_h
    非常感谢老师的付出!前面基础篇和提升篇的课程可以很快消化。实践篇里面的内容我则需要花比较多的时间去梳理对象之间的关系,然后才能弄清楚运行时各个对象之间的联系。正如前面有同学说老师虽然是用C语言写的代码,但是处处是面向对象的思想。
    我认为tcp-server与http-server是基类和子类关系;channel和acceptor也是基类与子类的关系。当然里面还存在大量的包含关系。tcp-connection类继承自channel,但是tcp-connection与eventloop的关系我就不是很确定。还想请老师或者其他同学能够指点迷津,最终很想将老师的c代码改写成c++风格,希望能跟各位同学相互讨论。email: leihao22@126.com

    作者回复: 非常支持c++改造,可以贴出代码地址大家一起review。

    2019-11-11
  • CCC
    真的非常谢谢老师,这个专栏我大多数文章都看了两遍以上,很多操作系统的细节关联的都搜了不少,很多以前只是了解的东西做到了真的理解了,再次谢谢老师!

    作者回复: 这是对我最大的奖励,感谢反馈

    2019-10-28
  • tt
    自己是做后端开发的,平常的开发工作也时常需要深入到TCP的底层去排除问题。学习完这个课程,真的是大大丰富了自己的网络知识细节。

    尤其是最后的实战部分。最近在研究PYTHON和JAVASCRIPT中的异步编程模型和事件循环,但对它们的底层实现细节不清楚,看了老师的实战代码,里面也有事件循环,也有CHANNEL等,觉得收获甚大,对这里提到的两种语言底层有了一个模糊的感性认识,为以后研读源码提供了一个入口。

    为此,自己也花了好长时间画了各个对象之间的关系图和应用启动后各对象之间的交互流程图。

    感谢老师!

    作者回复: 感谢支持,有收获是对我工作最大的肯定

    2019-10-28
    1
  • 传说中的成大大
    这是老师给我吗收拾的细软,让我们下山了吗?o(╥﹏╥)o

    作者回复: 江湖还在,继续交流哦

    2019-10-28
  • MoonGod
    谢谢老师的解答,整个系列受益匪浅。

    作者回复: 非常欣慰,感谢支持

    2019-10-28
收起评论
8
返回
顶部