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

04 | TCP三次握手:怎么使用套接字格式建立连接?

盛延敏 2019-08-09
你好,我是盛延敏,这里是网络编程实战第 4 讲,欢迎回来。
在上一讲里我们介绍了 IPv4、IPv6 以及本地套接字格式,这一讲我们来讲一讲怎么使用这些套接字格式完成连接的建立,当然,经典的 TCP 三次握手理论也会贯穿其中。我希望经过这一讲的讲解,你会牢牢记住 TCP 三次握手和客户端、服务器模型。
让我们先从服务器端开始。

服务端准备连接的过程

创建套接字

要创建一个可用的套接字,需要使用下面的函数:
int socket(int domain, int type, int protocol)
domain 就是指 PF_INET、PF_INET6 以及 PF_LOCAL 等,表示什么样的套接字。
type 可用的值是:
SOCK_STREAM: 表示的是字节流,对应 TCP;
SOCK_DGRAM: 表示的是数据报,对应 UDP;
SOCK_RAW: 表示的是原始套接字。
参数 protocol 原本是用来指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成。protocol 目前一般写成 0 即可。

bind: 设定电话号码

创建出来的套接字如果需要被别人使用,就需要调用 bind 函数把套接字和套接字地址绑定,就像去电信局登记我们的电话号码一样。
调用 bind 函数的方式如下:
bind(int fd, sockaddr * addr, socklen_t len)
我们需要注意到 bind 函数后面的第二个参数是通用地址格式sockaddr * addr。这里有一个地方值得注意,那就是虽然接收的是通用地址格式,实际上传入的参数可能是 IPv4、IPv6 或者本地套接字格式。bind 函数会根据 len 字段判断传入的参数 addr 该怎么解析,len 字段表示的就是传入的地址长度,它是一个可变值。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(63)

  • 假装在火星
    之前看过一些文章解释,为什么tcp建立连接需要三次握手,解释如下

    tcp连接的双方要确保各自的收发消息的能力都是正常的。
    客户端第一次发送握手消息到服务端,
    服务端接收到握手消息后把ack和自己的syn一同发送给客户端,这是第二次握手,
    当客户端接收到服务端发送来的第二次握手消息后,客户端可以确认“服务端的收发能力OK,客户端的收发能力OK”,但是服务端只能确认“客户端的发送OK,服务端的接收OK”,
    所以还需要第三次握手,客户端收到服务端的第二次握手消息后,发起第三次握手消息,服务端收到客户端发送的第三次握手消息后,就能够确定“服务端的发送OK,客户端的接收OK”,
    至此,客户端和服务端都能够确认自己和对方的收发能力OK,,tcp连接建立完成。

    作者回复: 赞

    2019-08-09
    4
    69
  • 阿卡牛
    这个问题的本质是, 信道不可靠, 但是通信双发需要就某个问题达成一致. 而要解决这个问题, 无论你在消息中包含什么信息, 三次通信是理论上的最小值. 所以三次握手不是TCP本身的要求, 而是为了满足"在不可靠信道上可靠地传输信息"这一需求所导致的

    作者回复: 赞。

    2019-08-09
    1
    29
  • 彭俊
    如何使用非阻塞调用套接字:使用fcntl函数设置套接字的属性fcntl(fd, F_SETFL, flags);
    非阻塞调用的 使用的场景:程序在调用返回之前,需要做其他事情,可以选择用定时轮询或事件通知的方式获取调用结果。
    是否可以调用bind 函数:可以,但是调用bind函数,也就是客户端指定了端口号,这样容易造成端口冲突,所以客户端不调用bind函数,让系统自动选择空闲端口比较好
    2019-08-11
    18
  • leesper
    思考题1:非阻塞调用的场景就是高性能服务器编程!我所有的调用都不需要等待对方准备好了再返回,而是立即返回,那么我怎么知道是否准备好了?就是把这些fd注册到类似select或者epoll这样的调用中,变多个fd阻塞为一个fd阻塞,只要有任何一个fd准备好了,select或者epoll都会返回,然后我们在从中取出准备好了的fd进行各种IO操作,从容自然 ^o^

    作者回复: 赞,下面的内容会讲到这部分了

    2019-08-19
    13
  • fjpcode
    1. 非阻塞套接字往往配合多路复用机制,达到提高CPU利用率,实现高并发的目的。
    2. 客户端做bind当然是可以的,但是因为连接请求往往是由客户端主动发起的,所以在客户端做bind显得不那么必要,还要承担端口冲突的风险。
    2019-08-09
    6
  • 鱼向北游
    关于三次握手
    几句话解释清楚
    1.信道不安全 保证通信需要一来一回
    2.客户端的来回和服务端的来回 共四次 这是最多四次
    3.客户端的回和服务端的来合并成一个,就是那个sync k ack j+1
    4.这样就是三次握手

    作者回复: 很清楚,很简洁。

    2019-09-02
    4
  • 一周思进
    刚好最近写了个通过man帮助手册编写基础tcp服务器
    https://mp.weixin.qq.com/s/bNdfXNQcZ3z_WzDWL5yQfA
    2019-08-09
    3
  • 星亦辰
    思考题2

    客户端可以bind 指定使用固定端口来连接。
    没有bind 则会产生一个随机的端口来完成连接请求。

    想到一个比较有意思的事情:

    客户端bind 以后,对内网进行端口扫描,表象则是,远程随机端口,到本地固定端口完成通信。看似,是本地开启了服务。如果bind 80 443 22这些常规端口,则可以迷惑安全人员 😄

    作者回复: 想多了,可以区分出来是不是本地监听端口(被动套接字)的,而且这个在大多数情况下不被允许的。

    2019-08-09
    3
    3
  • Knight²º¹⁸
    老师我有一个问题,编程语言中的IO模型和操作系统中的IO关系是什么?

    作者回复: 就是要和操作系统I/O打交道,编程语言的I/O模型是通过抽象和设计,总结出的一套规范。

    2019-08-11
    2
    2
  • 浦上清风
    1. 非阻塞 == 异步通信 ???
    2. 可以是可以,但是不安全???

    作者回复: 1 不是等价的
    2 没有必要,还徒增了端口冲突的危险。

    2019-08-09
    2
  • _stuView
    sockfd里的这个fd代表什么

    作者回复: file description,文件描述符。UNIX世界里万物皆文件。

    2019-08-09
    6
    2
  • 云端

    您好:老师

    int socket_init()
    {
           struct protoent* protocol = NULL;
            protocol=getprotobyname("icmp");
            int sock=socket(AF_INET,SOCK_RAW,protocol->p_proto);

    }

    在外部两次调用该函数,icmp协议原始套接字返回文件描述符相同,每次都是sock=5,为什么?怎么才能在外部多次调用该函数时返回的上下文描述符不一样?

    作者回复: 我的程序和结果 :

    #include "lib/common.h"

    int socket_init()
    {
        struct protoent* protocol = NULL;
        protocol=getprotobyname("icmp");
        int sock=socket(AF_INET,SOCK_RAW,protocol->p_proto);
        if(sock == -1){
            error(1,errno,"create socket failed");
        }
        return sock;
    }

    int main(int c, char **v) {
        printf("socket %d \n", socket_init());
        printf("socket %d \n", socket_init());
    }

    socket 3
    socket 4

    2019-09-10
    1
    1
  • 苦行僧
    顶上的留言非常完美的解释了为什么是三次握手
    2019-08-14
    1
  • zhchnchn
    这篇很是解惑,感谢。有2个问题想请教老师:
    1. `socket`函数的参数`domain`的值,在`bind`函数的参数`addr`的`sin_family`中也需要设置,这样不是重复了吗?
    2. `connect`函数出错返回可能的3种情况中,其中第1种“TIMEOUT 错误”和第3种“destination unreachable”,感觉都是连接不到服务端的IP,这两种情况有什么区别吗?

    作者回复: 1.两个不是一个东西,一个是套接字地址,一个是套接字,都需要设置。

    2.unreachable是被动收到了其他网络涉笔发来的ICMP报文信息,而timeout是在尝试一段时间后主动放弃的。

    2019-08-09
    3
    1
  • Keep-Moving
    老师,想问一下,为什么是三次握手,而不是四次、五次握手?

    作者回复: 因为三次握手是最简洁的方式,已经足够完成信息的确认。

    2019-08-09
    2
    1
  • 广训
    记得很久前学套接字,提供的例子就是客户端bind端口,基本第二次和以后在运行,就告诉端口被占用,那时候不懂,就换个端口再来一次

    作者回复: 这是有原因的,不需要每次都换端口,下面会降到具体解法。

    2019-08-09
    1
    1
  • 星亦辰
    非阻塞,一般是把请求接收到的套接字传递给子进程或子线程,然后,主进程,主线程继续等待下一个请求。多数网络服务器都是这个路子吧

    作者回复: 非阻塞,是指I/O模型,不是等待数据或连接,还是通过I/O通知的方式来感知数据或连接。

    后面的章节会详细讲述非阻塞模型

    2019-08-09
    1
  • 云师兄
    请问老师,如果服务端的端口开放但是没有监听的应用,客户端sync包到达后都是返回rst吗
    2019-12-11
  • Geek_68d3d2
    当套接字为SOCK_RAW的时候是不是必须要指定protocol的值?

    作者回复: 创建一个原始套接字SOCK_RAW。第三个参数(协议)通常不为0,一般为形如IPPROTO_xxx的某个常值。

    下面是一个例子:

    int sockfd;
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);

    2019-12-04
  • godtrue
    学完这节,感觉TCP的三次握手机制,更明白了一些,相信此生都会记得,养成了翻评论的习惯,感觉有些同学的解释比老师的还要通俗易懂。
    我的小结如下,假设只有客户端C和服务端S,两台机器:
    1:啥是TCP的三次握手机制?
    TCP的三次握手机制,是C和S使用TCP协议进行通信,他们在正式建立链接前,先进行了三次简单的通信,只有这三次简单的同学成功了,才建立正式的连接,之所以说是简单的通信,因为这三次通信发送的消息比较简单,就是固定格式的同步报文和确认报文

    2:TCP的三次握手机制的目的是啥?
    TCP协议的设计目标之一,就是保证通信信道的安全,而TCP的三次握手机制就是用于确认信道是否安全的手段之一,确认信道安全,最基本的首先要确认C和S都具备信息首发的能力吧!也就是C要确定S能收发信息,S要确定C能收发信息,咋确认呢?那就发送一条消息实验一下呗!所以,就有了TCP的三次握手机制,TCP的三次握手机制的核心目标就是要确认C和S之间的通信信道是安全的。

    3:为啥是三次?
    按道理来讲,C要确认S是否能够正确的收发消息,需要发生一条消息给S,然后接收到S的一条确认收到的消息才行,这一来一回就是两条消息。同理,S要确认C是否能够正确的收发消息,也需要这么玩。这样就需要两趟一来一回,总共需要四次通信,其实这么玩思维上一点负载都是没有的自然而然。
    不过只是为了确认C和S能否正常通信的话,就如此设计,被聪明一看到就会骂傻X,人类孜孜不倦所追求是更快、更高、更强,计算机世界中这种追求更加的强烈,将S的确认收到C发送的消息和S能正常发生的消息一次性的都发给C岂不是更好,虽然增加了点消息的内容,但是相对于消息的传输消耗而言还是非常少的,而且从整体消耗上看,是减少了一次通信的过程,性能想必会更好。
    很明显一次握手、两次握手都确认不了C和S的收发消息的能力是否OK。三次握手是比较简洁有效的方式,大于三次之上的握手机制也可以确认C和S是否能够正常通信,不过有些浪费资源了,毕竟三次就能搞定的事情,没必要搞三次至少,毕竟对于性能的追求我们是纳秒必争的。

    作者回复: 🐂。

    2019-11-20
收起评论
63
返回
顶部