1.什么是socket?
先看看官方回答:
对网络中不同主机上的应用进程之间进行双向通信(发送发|客户端和接收方|服务端) 的端点的抽象
套接字(socket)是一个抽象层(在应用层和传输层间的一层抽象),应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作(I/O)(例如)。
套接字允许应用程序将插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。
外观模式,只提供简单的接口,屏蔽内部复杂的实现
自己试着理解一下:
网络协议是一种抽象的东西,只规范了计算机间进行交换时数据流格式
但是在实际进行网络交互时,不光光只有网络协议(数据流格式),还应该有以下步骤
-
应用程序在内存中组装了一个http的request或response(符合http协议的大字符串|字节流)
- header、url、query、.....
-
有了需要发送的数据,需要将数据发送到目的地(需要利用硬件的网卡)
-
由于不能直接操作硬件网卡(也操作不来),因此通过告诉操作系统告诉他我们要发网络请求
-
由操作系统完成请求的发送
socket的作用就是操作系统提供的网络传输接口(在应用层和传输层中间) !(socket是以端口为维度,而不是请求维度)(我们通过socket接收到请求后,用线程(从池中获取)处理请求)
我们可以直接在高级语言中使用socket(高级语言封装后的)进行网络编程,也可以使用封装好的包(例如net包,但是底层仍然是调用操作系统socket)进行网络编程
socke发送数据的过程(宏观的)
- 获得内存中封装好的应用程序请求
- 根据设置的传输协议,把请求包装
- 将包装好后的数据复制到网卡上,并且发送数据(发送数据下面详细讲解)
当然socket不光光只可以发送http请求,还可以发起rpc调用....
2.socket通信
2.1通信函数
套接字API最初是作为UNIX操作系统的一部分而开发的,所以套接字API与系统的其他I/O设备集成在一起
当应用程序要为因特网通信而创建一个套接字(socket)时,操作系统就返回一个小整数作为描述符(descriptor)来标识这个套接字(可以对socket(绑定在端口上(一个进程))进行io的读写操作)
-
服务端初始化Socket
-
服务端与端口绑定(todo 进程和端口的关系?)\
-
服务端调用accept阻塞
-
客户端初始化Socket
-
客户端连接服务端
-
客户端写数据(发送)
-
服务端读数据(接收)
-
服务端处理结果(每天的工作)(其实服务端也没有对接真实端的流量,会经过lvs,经过Nginx反向代理后,最后落到实际的服务器上)
-
服务端写数据(发送)
-
客户端读数据(接收)
2.2socket()函数
-
作用
- socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket
- 后面都需要根据描述符进行一些读写操作(读request,写response)
-
参数(参数间不可随意组合)
-
protofamily 协议域
-
AF_INET(IPV4)
-
AF_INET6(IPV6)
-
AF_LOCAL(或称AF_UNIX,Unix域socket)
-
AF_ROUTE等等 \
-
-
type 指定socket类型\
-
SOCK_STREAM\
-
SOCK_DGRAM\
-
SOCK_RAW\
-
SOCK_PACKET\
-
SOCK_SEQPACKET\
-
-
protocol 应用层协议由应用程序标记\
-
IPPROTO_TCP\
-
IPPTOTO_UDP\
-
IPPROTO_SCTP\
-
IPPROTO_TIPC
-
-
2.3bind()函数
-
作用
- 绑定特定地址
- 不使用则随机分配一个
-
参数
-
sockfd socket描述字,它是通过socket()函数创建
-
addr sockfd的协议地址\
-
addrlen 地址的长度(IP地址+端口号)\
-
网络字节序与主机字节序
主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。
引用标准的Big-Endian和Little-Endian的定义如下:
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。
这种传输次序称作大端字节序。由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。
字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。
所以:在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。
由于这个问题曾引发过血案!公司项目代码中由于存在这个问题,导致了很多莫名其妙的问题,所以请谨记对主机字节序不要做任何假定,
务必将其转化为网络字节序再赋给socket。\
2.4listen()、connect()函数
-
listen()
-
作用:服务端使用,被动等待请求
-
参数
-
socket描述字\
-
相应socket可以排队的最大连接个数\
-
-
-
connect()
-
作用:客户端使用,主动发起请求(浏览器其实也是服务端程序 socket收发请求+静态资源渲染)
-
参数
-
客户端的socket描述字\
-
服务器的socket地址\
-
socket地址的长度\
-
-
2.5accept()函数
-
作用:服务端建立连接,可以进行网络IO,类似普通文件的IO
-
参数
- \
2.6read()、write()等函数
- 数据的读写操作(客户端和服务端)
2.7close函数
- 完成了读写操作就要关闭相应的socket(类似的文件的读写操作)\
2.8socket与三次握手与四次挥手(当协议为tcp时)
\
3.补充
3.1 http与rpc的对比
http curl与rpc调用的区别:
| http curl | rpc调用 | |
|---|---|---|
| 底层实现 | 封装好请求数据后,通过socket发送数据,通过socket监听数据 | |
| 序列化方式 | 使用http协议发送的是一个大字符串接收的也是一个大字符 | 有thrift协议pb协议..... |
| 入参 | 不确定 | 确定 |
| 出参 | 不确定,需要一层层解析json串 | 确定,可以在语言中通过对象使用 |
| 序列化效率 | 低 | 高 |
| 发送包大小 | 大 | 小 |
| 特点 | 可以基于rpc框架,进行限流熔断等服务治理 |
3.2进程和端口关系
端口是固定的,只能和一个线程|线程号绑定
查询端口号(知道端口号):lsof -i:端口号 可以查询对应的进程号
查询进程号(不知道端口号):ps aux|grep common
杀死进程:kill -9 pid
3.3文件读写后为什么要close(socket也是一种文件)
-
作用:
- 终止指定文件描述符与对应文件之间的关联
- 并释放该文件描述符,即该文件描述符可被重新使用
-
文件描述符
-
举个栗子
-
标准输入(standard input)的文件描述符是 0\
-
标准输出(standard output)是 1\
-
标准错误(standard error)是 2\
-
-
文件
-
FILE结构包括一个缓冲区和一个文件描述符\
-
io的文件对象,内存可能包含一个内存buffer(猜的,有待考证)
-