Socket

197 阅读4分钟

3.2

socket

socket就是对tcp/udp连接的一种包装, 相当于socket提供了api来建立和使用tcp/udp连接. 因此应用程序在建立tcp/udp连接的时候, 都是通过socket套接字完成的.

文件描述符

在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件、目录文件、链接文件和设备文件。文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符。

socket套接字就是一个文件描述符

socket套接字建立连接的准备过程

服务端:

  • 如果有一个进程期望建立TCP连接, 那么需要经历下面三个准备工作
  • (1) 建立socket套接字对象 A ----> socket()方法
  • (2) A绑定端口号, 通常该端口号是进程在启动的时候就已经申请好了的(如果进程不需要通讯, 是不需要端口号的, 端口号用于TCP/UDP的报文结构中). -----> bind()方法
  • (3) 进程监听A, 等到具体的连接到来 -----> listen()方法

客户端:

  • 如果一个客户端期望建立TCP连接, 需要经历下面两个准备工期
  • (1) 建立socket套接字对象 B ----> socket()方法
  • (2) 初始化B, 即为B填充 源ip + 源端口号 和 目标ip + 目标端口号 -----> connect()方法

socket套接字建立连接的连接过程(以建立TCP连接为例)

(1) B对象的connect()方法会向A发送TCP连接请求, A和B经历三次握手后, 服务端就会创建一个新的套接字C, C和B就建立了连接. 客户端用B, 进程用C就可以进行通讯.

(2) 套接字A进入阻塞状态, 等待下一次请求的到达, 进程也会继续监听A.

同一个端口号创建了多个TCP连接?

没错的, 同一个端口号只能由一个socket套接字A监听, 但是同一个端口号可以建立多个TCP连接. 具体过程就是服务端建立连接的套接字都是由A新生成的. 每个TCP连接由两个套接字(客户端一个, 服务端一个)组成, 会构成一个[client ip, client port, server ip, server port]的四元数组, 用来区分不同的连接, 用于唯一标识tcp连接.

通信都是通过同一端口号, 不同的连接如何接受自己的数据?

操作系统,接收到一个端口发来的数据时,会在该端口,产生的连接中,查找到符合这个唯一标识的并传递信息到对应缓冲区。 查找的方式就是通过[client ip, client port, server ip, server port]这个四元数组.

一个端口号最多能建立多少TCP连接?

如果是同一个端口号, 能建立多少socket套接字, 就能建立多少TCP连接, 一个进程会监听这个端口号, 也就是这个进程有多少能够使用的文件描述符, 就能建立多少TCP连接.

比如Linux用户进程默认最大只能有1024个文件描述符。内核进程默认最大有4096个文件描述符。

进程使用socket套接字进行通讯的过程

首先客户端和服务端已经得到建立好连接的套接字.

客户端:

利用send()方法, 发送数据

服务端:

通过端口接受数据, 解析得到四元组, 识别到是哪个tcp连接, 将数据给相应的套接字, 进程调用recv()方法获得数据.

当通讯完毕后, 两个套接字四次挥手后断开连接, 两个套接字失效.

同一端口的多个已经建立的TCP连接, 如何知道哪个TCP连接有数据传送过来?

进程会采用select、poll、epoll方法来得到tcp连接正在传输数据, 这三个方法都属于IO多路复用范畴

socket的读写队列

每个Socket都在内核空间中都有与之相关联的读写队列(存储空间),一个读队列,一个写队列。且读队列的大小一般要大于写队列。Socket要读数据就从对应的读队列中读,写数据就写到相应的写队列。

同一端口可以由两个进程监听吗?

正常情况下, 一个端口只能由一个进程监听, 但是linux内核支持端口复用了, 也就是允许两个套接字同时监听同一个端口号, 但是需要设置端口重用选项. 就可以同一时刻使用两个监听进程/线程分别去监听它们,客户端发来的连接也就可以通过round-robin的均衡算法轮流地被接待。

因此端口重用一般是两个一摸一样的进程来重用一个端口, 不然端口数据就混乱了.