计算机网络的那些事儿-3
C++软件与嵌入式软件面经解析大全(蒋豆芽的秋招打怪之旅)
本章讲解知识点
- 1.1 什么是计算机网络
- 1.2 计算机网络的组成
- 1.3 分组交换
- 1.4 计算机网络的类别及性能指标
- 1.5 计算机网络的结构体系
- 1.6 TCP/IP四层模型及常见协议
- 1.7 TCP和UDP的区别
- 1.8 TCP三次握手与四次挥手
- 1.9 TCP可靠机制
- 1.10 socket网络编程
- 1.11 TCP粘包
- 1.12 HTTP协议
- 1.13 GET和POST
- 1.14 HTTP的特点
- 1.15 HTTP1.0、HTTP1.1、HTTP2.0的区别
- 1.16 HTTPS协议
受众:本教程适合于C/C++已经入门的学生或人士,有一定的编程基础。
本教程适合于互联网、嵌入式软件求职的学生或人士。
故事背景
蒋 豆 芽:小名豆芽,芳龄十八,蜀中人氏。卑微小硕一枚,科研领域苟延残喘,研究的是如何炒好一盘豆芽。与大多数人一样,学习道路永无止境,间歇性踌躇满志,持续性混吃等死。会点编程,对了,是面对对象的那种。不知不觉研二到找工作的时候了,同时还在忙论文,豆芽都秃了,不过豆芽也没头发啊。
隔壁老李:大名老李,蒋豆芽的好朋友,技术高手,代码女神。给了蒋豆芽不少的人生指导意见。
导 师:蒋豆芽的老板,研究的课题是每天对豆芽嘘寒问暖。
故事引入
“吁吁吁吁”。。。
本来豆芽在好好复习面经,一阵短信铃声惊醒了豆芽。豆芽心想,嗯哼?难道又是感谢信来了?怀着忐忑不安的情绪,豆芽点开了短信,瞬间豆芽就石化了。
亲爱的豆芽:
您好,
恭喜您已经完成京西2021校园招聘的所有面试流程,目前正在给您进行offer审批,如审批通过,会给您发送正式校招offer;如审批不通过,会给您发送拒信。
如有任何问题您可发邮件咨询,坚信您对京西的每一次关注,都将促进我们更快相遇~
始终关注你的
Tony老师
燃 | 京西校招
Email:campus@jx.com
京西来消息了啊!!!豆芽想去的京西公司,梦寐以求的大公司,豆芽高兴地跳起来了!!!豆芽简直难以平复自己激动的心情。。。
蒋 豆 芽:(傻笑)上海,嘿嘿嘿,年薪三十万,嘿嘿嘿,朝九晚五,嘿嘿嘿。。。
隔壁老李:(疑惑)豆芽,豆芽,豆芽,你怎么了?
蒋 豆 芽:(傻笑)大公司,嘿嘿嘿,京西,嘿嘿嘿。。。
隔壁老李:(叹气)完了,秋招又疯一个。
蒋 豆 芽:(擦擦口水)诶,老李,你怎么在旁边?
隔壁老李:怎么了,豆芽,是有什么好事情吗?
京西嘛,来消息了,阿巴阿巴阿巴。。。。。。
隔壁老李:原来是这样啊,恭喜恭喜啊。但是也不能高兴太早啊。我看看短信结尾:始终关注你的Tony老师???
蒋 豆 芽:怎么是Tony老师啊?不管了,反正是我想去的大公司。
隔壁老李:京西嘛,不好说,能不能拿到正式offer真的是个未知数,从历年数据来看,京西的池子太深了,可不敢赌啊。
蒋 豆 芽:啊!??这样的吗?
隔壁老李:是啊,特别是今年疫情影响,不到最后签三方,还真不好说啊。不过也不要紧,还要继续准备其他公司,命里有时终须有,命里无时莫强求。
蒋 豆 芽:(噘嘴)哼,我偏要勉强!害,好难啊,我还以为我可以结束秋招了。
隔壁老李:万里长征才刚开始呢!豆芽,你还要继续加油啊!
蒋 豆 芽:没问题,老李!对了,老李,上一节学了TCP/IP模型,这次你给我讲讲怎么应用吧。
1.10 socket网络编程
隔壁老李:好,没问题,网络编程是面试官喜欢考的内容。赶紧上车,我们继续新旅程!
隔壁老李:首先我们先明确一个问题,网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起。
在本地可以通过进程 PID 来唯一标识一个进程,但是在网络中这是行不通的,因为每个主机都有各自的进程PID,就不唯一了。而TCP/IP 协议族解决了这个问题,在同一局域网下,网络层的 IP 地址可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。 这样利用三元组 (Ip 地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其他进程进行交互。
隔壁老李:我们之前讲进程通信时提到过,socket可以实现不同主机之间的通信。socket是一种特殊的文件。在Linux中,“一切皆文件”,都可以用“打开(open)——读写(write/read)——关闭(close)”的模式来操作。socket就是改模式的一个实现,并提供了一系列对应的函数接口。
隔壁老李:我们以TCP为例,下图展示了其交互的过程:
图中展示的交互流程,具体如下所述 :
(1)服务器根据地址类型( ipv4, ipv6 )、 socket 类型、协议创建 socket。
(2)服务器为 socket 绑定 IP 地址和端口号。
(3)服务器 socket 监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket 并没有被打开 。
(4)客户端创建 socket。
(5)客户端打开 socket,根据服务器 IP 地址和端口号试图连接服务器 socket。
(6)服务器 socket 接收到客户端 socket 请求,被动打开,开始接收客户端请求,直到客户端返回连接信息 。这时候 socket 进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端连接请求 。
(7)客户端连接成功,向服务器发送连接状态信息 。
(8)服务器 accept 方法返回,连接成功 。
(9)客户端向 socket 写入信息 。
(10)服务器读取信息 。
(11)客户端关闭 。
(12)服务器端关闭 。
仔细一看,服务器 socket 和客户端 socket 建立连接的部分其实就是 3 次握手。
蒋 豆 芽:原来是这样!
隔壁老李:下面我们来介绍基本的接口函数:
socket函数:socket 函数对应于普通文件的打开操作 。 普通文件的打开操作返回一个文件描述字,而socket() 用于创建一个 socket 描述符( socket descriptor),它唯一标识一个 socket。 这个 socket 描述符跟文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
原型如下:
int socket(int domain, int type, int protocol); //参数 /************************************************************************************************************************************** (1)domain :即协议域,又称为协议族( family )。常用的协议族有: AF_INET、 AF_INET6 、 AF_LOCAL (或称 AF_UNIX, Unix 域 socket )、AF_ROUTE 等。 协议族决定了 socket的地址类型,在通信中必须采用对应的地址,如 AF_INET 决定了要用 ipv4 地址 (32位)与端口号(16位)的组合 、 AF_UNIX 决定了要用一个绝对路径名作为地址 。 (2)type :指定 socket 类型。常用的 socket 类型有: SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET 、 SOCK_SEQPACKET等 。其中,SOCK_STREAM 表示提供面向连接的稳定数据传输,即 TCP 协议。 SOCK_DGRAM 表示使用不连续、不可靠的数据包连接。 (3)protocol :指定协议。 常用的协议有, IPPROTO_TCP, IPPTOTO_UDP, IPPROTO_SCTP、 IPPROTO_TIPC 等,它们分别对应 TCP 传输协议、UDP 传输协议、STCP 传输协议 、TIPC 传输协议。 返回值:成功返回一个非负套接字,失败返回-1 **************************************************************************************************************************************/
当调用 socket 创建 一 个 socket 时,返回的 socket 描述符它存在于协议族( address family, AF_XXX)空间中,但没有一个具体的地址。 如果想要给它赋予一个地址,就必须调用 bind()函数,否则系统就在调用 connect()、 listen()时自动随机分配一个端口 。
如果调用成功就返回新创建的套接字的描述符,如果失败就返回 INVALID_SOCET(Linux 下失败返回-1)。 套接字描述符是一个整数类型的值 。 每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系 。 该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表,但是套接字数据结构都存放在操作系统的内核缓冲里 。
bind函数: bind() 函数把一个地址族中的特定地址绑定给 socket。
原型如下:
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); //参数 /************************************************************************************************************************************** (1)sockfd :即 socket 描述字,它是通过 socket()函数创建来唯一标识一个 socket 的 。bind()函数就是将给这个描述字绑定一个名字 。 (2)addr : 一个 const struct sockaddr* 指针,指向要绑定给 sockfd 的协议地址。 这个地址结构根据地址创建 socket 时的地址协议族的不同而不同。 (3)addrlen :对应的是地址的长度。 返回值:成功返回0,出错返回-1 **************************************************************************************************************************************/
listen和connect函数
如果作为一个服务器,在调用 socket()、 bind()之后就会调用 listen()来监听这个 socket,如果客户端这时调用 connect()发出连接请求,服务器端就会接收到这个请求 。 listen 和connect 的函数原型是:
int listen(int sockfd, int backlog); //参数 /************************************************************************************************************************************** (1)sockfd :要连接的 socket 描述字 (2)backlog : 相应 socket 可以排队的最大连接个数 返回值:成功返回0,出错返回-1 **************************************************************************************************************************************/ int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); //参数 /************************************************************************************************************************************** (1)sockfd :要监听的 socket 描述字 (2)servaddr :服务器的 socket 地址 (3)addrlen :socket 地址的长度 返回值:成功返回0,出错返回-1 **************************************************************************************************************************************/
accept函数
TCP 服 务器端依次调用 socket()、 bind ()、 listen ()之后,就会监听指定的 socket 地址了 。 TCP 客户端依次调用 socket()、 connect()之后就会向 TCP 服务器发送了一个连接请求 。TCP 服务器监听到这个请求之后,就会调用 accept()函数取接收请求,这样连接就建立好了 。 之后就可以开始网络 I/O 操作了,即类同于普通文件的读写 I/O 操作 。
函数原型如下:
int accept(int sockfd, const struct sockaddr *cliaddr, socklen_t addrlen); //参数 /************************************************************************************************************************************** (1)sockfd :要监听的 socket 描述字 (2)cliaddr :客户端的 socket 地址 (3)addrlen :socket 地址的长度 返回值:成功返回0,出错返回-1 **************************************************************************************************************************************/
read和write函数
至此服务器与客户已经建立好连接了,可以调用网络 I/O 进行读写操作了,即实现了网络中不同进程之间的通信。 网络 I/O 操作有下面几组:
read()/write() recv()/send() readv()/writev() recvmsg()/sendmsg() recvfrom()/sendto()
最常用的则是 read()和 write() 。 read()的函数原型是:
ssize_t read(int fd, void *buf, size_t count); //参数 /************************************************************************************************************************************** (1)fd :文件描述字 (2)buf :缓冲区 (3)count :缓冲的长度 返回值:成功时返回读的字节数,出错返回-1 **************************************************************************************************************************************/
write()的函数原型是:
ssize_t write(int fd, void *buf, size_t count); //参数 /************************************************************************************************************************************** (1)fd :文件描述字 (2)buf :缓冲区 (3)count :缓冲的长度 返回值:成功时返回写的字节数,出错返回-1 *****
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> - 本专刊适合于C/C++已经入门的学生或人士,有一定的编程基础。 - 本专刊适合于互联网C++软件开发、嵌入式软件求职的学生或人士。 - 本专刊囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构等一系列知识点的讲解,并且最后总结出了高频面试考点(附有答案)共近400道,知识点讲解全面。不仅如此,教程还讲解了简历制作、笔试面试准备、面试技巧等内容。 </p> <p> <br /> </p>