Socket(套接字) = IP地址:端口号,在网络层通过IP地址找到主机,在传输层通过端口号找到应用进程。每一个传输层连接唯一的被通信两端的两个套接字所确定。
python中,通过socket()函数创建套接字,socket()函数语法格式:
socket.socket([family[, type[, proto]]])
其中的参数:
family:套接字家族,可以使用 AF_UNIX 或者 AF_INET(IPv6用AF_INET6)等。
type:套接字类型,主要有(TCP) SOCK_STREAM、 (UDP)SOCK_DGRAM,还有一些其他类型。
proto:一般不填默认为 0。
例如,创建TCP套接字可写成:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
对象 | 描述 |
服务器端套接字 | |
s.bind() | 绑定address即(ip,port)到套接字,在AF_INET下,以元组(ip,port)形式表示地址。 |
s.listen() | 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来。 |
客户端套接字 | |
s.connect() | 主动初始化TCP服务器连接,一般address的格式为(host,port),若连接出错,返回socket.error错误。 |
s.connect_ex() | connect()的扩展版本,出错时返回错误码,而不是抛异常。 |
公共用途的套接字 | |
s.recv() |
接收TCP数据 |
s.send() | 发送TCP数据 |
s.sendall() | 完整发送TCP数据 |
s.recvfrom() | 接收UCP数据,与recv()类似,但返回值是(data,adderss)。 data包含接收数据的字符串, address是发送数据的套接字地址。 |
s.sendto() | 发送UCP数据,将数据发送到套接字,address是元组(host,port),指定远端地址。返回值是发送的字节数。 |
s.close() | 关闭套接字 |
s.getpeername() | 返回套接字的远程地址,返回值通常是一个元组(hostaddr,port)。 |
s.getsockname() | 返回套接字自己的地址,返回值通常是一个元组(hostaddr,port)。 |
s.setsockopt() | 设置给定套接字选项的值。 |
s.getsockopt() | 返回套接字选项的值。 |
s.settimeout() | 设置套接字操作的超时时间,timeout是一个浮点数,单位是秒。值为None则表示永远不会超时。一般超时期应在刚创建套接字时设置。 |
s.gettimeout() | 返回当前超时值,单位是秒,如果没有设置超时值则返回None。 |
s.fileno() | 返回套接字的文件描述。 |
s.setblocking(flag) | 如果flag为0,则将套接字设置为非阻塞模式,否则将套接字设置为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引发socket.error异常。 |
s.makefile() | 创建一个与该套接字相关的文件。 |
实现socket通信的流程:
服务器端tcp_server.py:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = '127.0.0.1'
port = 8089
# 注意!bind()函数里参数是一个元组
s.bind((host, port))
s.listen(1)
# 有客户端连接上来,返回一个新的socket和地址
c, addr = s.accept()
print('连接地址是:{}'.format(addr))
# 给连接上来的客户端发消息~
c.send(bytes('欢迎你啊~叶天才~~撒花✿✿ヽ(°▽°)ノ✿', 'utf-8'))
# 打印收到客户端发来的消息
print(c.recv(1024).decode('utf-8'))
s.close()
服务器端先初始化socket,然后bind绑定指定端口来提供服务,以便客户端向指定端口发送请求,紧接着就是监听端口,等待客户端连接。
客户端tcp_client.py:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = '127.0.0.1'
port = 8089
# 注意!这里connect()方法里参数也是个元组
s.connect((host, port))
b = s.recv(1024)
# 打印收到服务器发来的消息
print(b.decode('utf-8'))
# 给服务器发送消息,要发送的是字节序列
s.send(bytes('我连上来啦,哈哈哈哈哈哈哈嗝~', 'utf-8'))
s.close()
客户端初始化socket,无需调用bind,一般由操作系统直接完成。发送请求前,操作系统为套接字随机分配一个可用的端口,同时将该套接字和本地地址信息绑定。
客户端发起connect连接,和服务器端的accept,共同完成tcp三次握手。
随后就可以进行数据通信,通信完毕后,close进行tcp四次挥手,结束通信。
先执行服务器端tcp_server.py,然后执行客户端tcp_client.py,最终执行结果
tcp_server.py打印:
连接地址是:('127.0.0.1', 49672)
我连上来啦,哈哈哈哈哈哈哈嗝~
tcp_client.py打印:
欢迎你啊~叶天才~~撒花✿✿ヽ(°▽°)ノ✿
创建socket和关闭socket这两步可以简化为:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
with语句会自动帮我们调用close方法关闭socket。
参考:
Socket通信原理_ziyonghong的博客-CSDN博客_socket通信原理