Socket介绍
假设我们需要编写一个C/S架构的程序,实现数据交互,就需要使用到OSI七层协议,由于它的缺点是分层太多,增加了网络工作的复杂性,所以没有大规模应用。后来人们对 OSI 进行了简化,合并了一些层,最终只保留了 4 层,从下到上分别是接口层、网络层、传输层和应用层,这就是 TCP/IP 模型。
而socket(套接字)是在应用程序的传输层和应用层之间抽象出了一个层叫做socket抽象层,可以理解为TCP/IP协议栈提供的对外的操作接口,即应用层通过网络协议进行通信的接口。Socket其实就是一个门面,它把复杂的TCP/IP协议族隐藏在Socket接口后面,不需要自己处理每一层。
什么是Socket
- Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
- 在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面
- 对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
- 所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
- 也有人将socket说成ip+port
- ip是用来标识互联网中的一台主机的位置
- 而port是用来标识这台机器上的一个应用程序
- ip地址是配置到网卡上的
- 而port是应用程序开启的
- ip与port的绑定就标识了互联网中独一无二的一个应用程序
- 而程序的pid是同一台机器上不同进程或者线程的标识
套接字分类
(1) 基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,
可以通过访问同一个文件系统间接完成通信
(2) 基于网络类型的套接字家族
套接字家族的名字:AF_INET
AF_INET(又称 PF_INET)是 IPv4 网络协议的套接字类型,AF_INET6 则是 IPv6 的
还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET
Socket套接字使用
套接字Socket可以使用不同的网络协议进行端对端的通信,主要是TCP和UDP协议,使用的流程如下图所示
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
如何创建套接字
套接字 Socket 实质上提供了主机间进程通信的连接点。进程通信之前,双方首先必须各自创建一个连接点。否则是没有办法建立联系并相互通信的。Python 中,我们用 socket()
函数来创建套接字,语法格式如下:
import socket
socket.socket(socket_family,socket_type,protocal=0)
socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。
# 获取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。
Socket 对象(内建)方法
函数 | 描述 |
---|---|
服务器端套接字 | |
s.bind() | 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。 |
s.listen() | 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来 |
客户端套接字 | |
s.connect() | 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
公共用途的套接字函数 | |
s.recv() | 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 |
s.send() | 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。 |
s.sendall() | 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
s.recvfrom() | 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
s.sendto() | 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 |
s.close() | 关闭套接字 |
s.getpeername() | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 |
s.getsockname() | 返回套接字自己的地址。通常是一个元组(ipaddr,port) |
s.setsockopt(level,optname,value) | 设置给定套接字选项的值。 |
s.getsockopt(level,optname[.buflen]) | 返回套接字选项的值。 |
s.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect()) |
s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 |
s.fileno() | 返回套接字的文件描述符。 |
s.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 |
s.makefile() | 创建一个与该套接字相关连的文件 |
如何为套接字绑定主机及端口
一个完整的 Socket 可以用一个通信双方的相关描述:
协议 , 本地地址 , 本地端口 , 远程地址 , 远程端口
实际应用中,在创建一个 Socket 时先用一个半相关描述(服务器这一半可以确定,而另一半尚不确定):
协议 , 本地地址 , 本地端口
每一个 Socket 有一个本地的唯一端口号,由操作系统分配。
绑定指为套接字绑定地址包含主机及其端口。 在 AF_INET 下,以元组(host,port)的形式表示地址。
-
host:用字符串表示主机的 IP 地址。表示本机'',也可用 127.0.0.1 表示回环地址,或者主机的一般 IP 地址。
-
port:端口号,数字表示。1024 以下为系统约定,自定义的用 1024 以上。
绑定通过套接字的绑定方法 bind() 来完成,输入参数为元组 (host,port)。
绑定示例:
my_socket.bind(('127.0.0.1', 1234)) # 绑定本地回环地址
my_socket.bind(('', 1234)) # 自动获取IP地址
如何设置套接字监听
服务器程序在调用创建套接字 socket() 和绑定 bind() 之后需要处于监听状态,因为不知客户端什么时候开始进行请求连接。为此,需调用套接字的监听方法 listen()。
一个服务端可能同时面对多个客户端的连接请求,为此服务器程序需创建一个连接队列来保存的连接请求,并依次为连接请求建立相应连接。为此需设置队列的大小作为监听方法的参数。
监听示例:
my_socket.listen(10) # 设置连接队列大小为10,并使套接字处于监听状态。
标签:Socket,编程,TCP,地址,抽象,接字,port,socket
From: https://www.cnblogs.com/xiao01/p/17968528