【计算机网络】Socket

Socket 介绍

概述

socket是一种IPC方法,它允许位于同一主机或使用网络连接起来的不同主机的应用程序之间交换数据。

socket进行通信的方式如下:

  • 各个应用程序创建一个socket,socket是一个允许通信的“设备”,两个应用程序都需要用到它。
  • 服务器将自己的socket绑定到一个众所周知的地址上使得客户端能够定位到它的位置。

使用socket()系统调用能够创建一个socket,它返回一个用来在后续系统调用中引用该socket的文件描述符。

fd = socket(domain, type, protocol)

通信domain

socket存在于一个通信domain中,它确定:

  • 识别出一个socket的方法(即socket“地址”的格式)
  • 通信范围(是同一主机,还是网络中不同主机)

现在操作系统支持下列domain:

  • UNIX(AF_UNIX)domain:允许同一主机上的应用程序之间进行通信
  • IPv4(AF_INET)domain
  • IPv6(AF_INET6)domain
socket domain

socket类型

每个socket实现都至少提供了两种socket:流和数据报。这两种类型在UNIX和Internet domain中都得到了支持。

socket 类型及其属性
流socket(SOCK_STREAM)

流socket提供了一个可靠的双向的字节流通信信道:

  • 可靠的
  • 双向的
  • 字节流
数据报socket(SOCK_DGRAM)

数据报socket允许数据以数据报的形式进行交换。在使用时无需与另一个socket简历连接。

socket 系统调用

socket()
int socket(domain, type, protocol)
  • 创建一个新的socket
  • type 指定socket类型,创建流socket通常指定为 SOCK_STREAM,数据报socket指定为SOCK_DGRAM
  • protocol默认为0
  • 返回socket的文件描述符
bind()
int bind(sockfd, struct sockaddr *addr, socklen_t addrlen)
  • 将一个socket绑定到一个地址上,通常,服务器需要使用这个调用来将其socket绑定到一个地址上供客户端能够定位到该socket上
  • addr参数是一个指针,指向socket绑定到的地址结构上,这个参数的结构类型取决于domain。
  • 服务器可以不调用bind()直接调用listen(),这将会导致内核为该socket选择一个临时端口
  • 客户端一般不需要调用,操作系统会自动绑定
通用socket地址结构:struct sockaddr

传入bind()的addr比较复杂,每种socket domain都使用了不同的地址格式,如UNIX domain socket使用路径名,而Internet domain socket 使用IP地址和端口号。struct sockaddr适用于所有domain,将各种domain特定的地址结构转换成单个类型以供socket系统调用中的各个参数使用。

Paste_Image.png
listen()
  • 允许一个流socket接受来自其他socket的接入连接
accept()
  • 在一个监听流socket上接受来自一个对等应用程序的连接,并可选的返回对等socket地址
connect()
  • 简历与另一个socket之间的连接

在大多数Linux架构上,所有这些socket系统调用实际上被实现成了通过单个系统调用socketcall()进行多路复用的库函数。

socket I/O 可以使用传统的read()和write()系统调用或使用一组socket特有的系统调用send() recv() sendto() recvfrom()。默认情况下,这些系统调用在I/O操作无法被立即完成时阻塞,使用fcntl() F_SETFL 操作用启用 O_NONBLOCK 打开文件状态标记可以执行非阻塞I/O

流socket

流程
  • 每个应用程序都必须创建一个socket
  • 服务端程序调用bind()将socket绑定到一个地址上,然后调用listen()通知内核它接受接入连接的意愿。
  • 其他客户端程序通过connect()建立连接,同时指定需连接的socket的地址
  • 服务端程序使用accept()接受连接。
  • 建立连接后,进行双向数据传输直到其中一个使用close()关闭连接为止。
  • 通信是通过传统的read()和write()系统调用或通过额外的一些socketAPI(send() recv())
流socket系统调用流程
监听接入连接:listen()
int listen(sockfd, backlog)

listen()系统调用将文件描述符sockfd引用的流socket标记为被动,这个socket后面会被用来接受来自其他(主动的)socket的链接。

无法再一个已连接的socket(已成功执行connect()的socket或由accept()调用返回的socket)上执行 listen()

如果服务器正忙于处理其他客户端,那么客户端的connect()可能并不能马上被accept(),这将产生一个未决的连接。

一个未决的socket连接

内核必须要记录所有未决的连接请求的相关信息,backlog参数允许限制这种未决连接的数量。在这个限制之内的连接请求会立即成功,之外的连接请求就会阻塞直到一个未决的连接被接受,并从未决连接队列中删除。

接受连接:accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

accept()系统调用会文件描述符sockfd引用的监听流socket上接受一个连入连接。如果在调用accept时不存在未决的连接,那么调用会阻塞直到有连接请求到达为止。

返回的结果是已连接的socket的文件描述符。addr参数指向一个用来返回socket地址的结构。

连接到对等socket:connect()
int connect()
流socket I/O

一对连接的流 socket 在两个端点之间提供了一个双向通信信道。

双向通信信道
连接终止:close()

关闭一个连接之后,对等应用程序读取数据时将会收到文件结束(所有缓冲数据都读取之后),如果要写入数据,会收到一个SIGPIPE信号,并且系统调用返回EPIPE错误。

数据报 socket

流程
  • 需要发送和接受数据报的应用程序都需要使用socket()创建一个数据报socket
  • 服务器将socket绑定到IP地址上,客户端通过该地址发起通信。
  • 要发送一个数据报,客户端调用sendto()
  • 服务端调用recvfrom(),在没有数据报时会阻塞。
  • 不再需要是,调用close()

无法保证顺序,也无法保证能够到达。由于底层协议有时会重新传包,也可能多次到达。

数据报流程
交换数据报 recvfrom() sendto()
在数据报socket上使用connect()

尽管数据报socket是无连接的,但在数据报socket上应用connect()系统调用仍然起作用,会导致内核记录这个socket的对等socket地址。

当一个数据报socket已连接后:

  • 数据报的发送可在socket上使用write和send

Socket UNIX DOMAIN

UNIX domain socket地址:struct sockaddr_un

在UNIX domain中,socket地址以路径名来表示,domain特定的socket地址结构的定义如下:

结构

为将一个UNIX domain socket绑定到一个地址上,需要初始化一个sockaddr_un结构,然后将指向这个结构的一个指针作为addr参数传入bind()并将addrlen指定为这个结构的大小。

当用来绑定UNIX domain socket时,bind()会在文件系统中创建一个条目,作为socket路径名的一部分的目录需要可访问和可写。这个文件会被标记为一个socket,当再这个路径名上应用stat()时,它会在stat结构的st_mode字段中的文件类型部分返回值S_IFSOCK。

尽管UNIX domain socket是通过路径名来标识的,但这些socket上发生的I/O无须对底层设备进行操作。

有关绑定一个UNIX domain socket的注意点:

  • 无法将一个socket绑定到一个既有路径名上
  • 通常将一个socket绑定到一个绝对路径上,这样这个socket就会位于文件系统中的一个固定地址处
  • 一个socket只能绑定一个路径名
  • 无法使用open()打开一个socket
  • 当不再需要一个socket时可以使用unlink()删除其路径条目

UNIX domain中的流socket

服务器流程:

  • 创建一个socket
  • 删除所有与路径名一致的既有文件,这样就能将socket绑定到这个路径名上
  • 为服务器socket构建一个地址结构,将socket绑定到该地址上,将这个socket标记为监听socket
  • 执行一个无线循环来处理进入的客户请求,每次循环迭代执行以下任务:
    • 接受一个连接,为该连接获取一个新的socket cfd
    • 从已连接的socket中读取所有数据并将这些数据写入到标准输出中
    • 关闭已连接的socket cfd
  • 服务器手动终止(发送一个信号)

客户端流程:

  • 创建一个socket
  • 为服务器socket构建一个地址并连接到位于该地址处的socket
  • 执行一个循环,将其标准输入复制到socket连接上,当遇到标准输入中的文件结尾时客户端就终止,其结果是客户端socket将会关闭并且服务器从连接的另一端的socket中读取数据时会看到文件结束。

UNIX domain中的数据报socket

对于UNIX domain socket来说,数据报的传输是在内核中发生的,也是可靠的,所有消息都会按序被递送并且不会发生重复的状况。

服务器创建socket后并绑定后,进入一个无线循环,在循环中使用recvfrom()接收来自客户端的数据报,将接收到的文本转换成大小格式并使用通过recvfrom()获取的地址将转换过的文本返回给客户端。

UNIX domain socket权限

socket文件的所有权和权限决定了哪些进程能够与这个socket进行通信

  • 要连接一个UNIX domain流socket需要在该socket文件上拥有写权限
  • 要通过一个UNIX domain数据报socket发送一个数据报需要在该socket文件上拥有写权限
  • 需要在存放socket路径名的所有目录上都拥有执行权限

创建互联socket对:socketpair()

有时候让单个进程创建一对socket并将它们连接起来是比较有用的。

int socketpair(int domain, int type, int protocol, int sockfd[2])

Linux抽象socket名空间

允许将一个UNIX domain socket绑定到一个名字上但不会在文件系统中创建的名字

  • 无需担心与文件系统中的既有名字产生冲突
  • 没有必要在使用完socket之后删除socket路径名,当socket被关闭之后会自动删除这个抽象名
  • 无需为socket创建一个文件系统路径名了,这对于chroot缓解以及在不具备文件系统上的写权限时比较有用的。

SOCKET:TCP/IP 网络基础

网络协议和层

TCP/IP套件中的协议
分层通信
TCP/IP协议层中的封装

数据链路层

要传输数据,数据链路层需要将网络层传递过来的数据报封装进被称为帧的一个一个单元。最大传输单元MTC是改层所能传输的帧大小的上限。

网络层 IP

网络层任务:

  • 将数据分解成足够小的片段以便数据链路层进行传输
  • 在因特网上路由数据
  • 为传输层提供服务

网络层的协议是IP,IPv4使用32位地址来标识子网和主机,IPv6则使用了128位的地址。

一个裸socket(SOCK_RAW),允许程序直接与IP层进行通信,但大多数都会基于一种传输层协议之上的socket。

IP传输数据报

IP以数据报(包)的形式来传输数据。在两个主机之间发送的每一个数据报都是在网络上独立传输的,它们经过的路径可能会不同。一个IP数据报包含一个头,其大小范围为20字节到60字节。包含目标主机的地址,源地址。

一个IP实现可能会给它所支持的数据报的大小设定一个上限。所有IP实现都必须做到数据报的大小上限至少与规定的IP最小重组缓冲区大小一样大。IPv4限制值是576字节,IPv6是1500字节。

IP是无连接和不可靠的

IP是一种无连接协议,并没有在相互连接的两个主机之间提供一个虚拟电路。

IP是一种不可靠的协议:尽最大可能将数据报从发送者传输给接收者,但并不保证包到达的顺序与它们被传输的顺序一致,也不保证是否重复,甚至到达。IP也美誉错误恢复。可靠性是通过使用TCP来保证的。

IPv4为IP头提供了一个校验和,这样能够检测出头中的错误,但并没有为包中所传输的数据提供任何错误检测机制。IPv6并没有为IP头提供校验和,它依赖高层协议来完成错误检测和可靠性。

IP数据报的重复使可能发生的,数据链路层采用一些技术确保可靠性以及IP数据报可能会以隧道形式穿越采用了重传机制。

IP对数据报进行分段

IP会将数据报分段成一个个大小合适的传输单元,这些分段在到达最终目的之后会被重组成原始的数据报(每个IP分段本身就包含一个偏移量)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容