TCP和UDP两个协议下的套接字

TCP和UDP对比

  • TCP(Transmission Control Protocol)
    • 可靠的、面向连接的协议。低效的全双工通信(接收缓存、发送缓存)。面向字节流。
    • 应用:Web浏览器,文件传输程序
  • UDP(User Datagram Protocol)
    • 不可靠的、无连接的协议。传输效率高(发送时延小)。一对一、一对多、多对一、多对多,面向报文(数据包)。尽力而为的传输,无拥塞控制。
    • 应用:域名系统(DNS)、视频流。

套接字的函数

s是一个socket对象。

服务端套接字函数

s.bind() 绑定(主机,端口号)到套接字
s.listen() 监听端口
s.accept() 阻塞式,等待TCP客户端的连接

客户端套接字函数

s.connect() 向服务端发起连接
s.connect_ex() connect()函数的扩展版,错误时返回错误码,而不是抛出异常。

公共用途套接字函数

s.recv() TCP接收数据
s.send() TCP发送数据,待发送数据量大于缓存区剩余空间时,数据丢失,不会发完。
s.sendall() TCP发送完整数据,本质是循环调用send函数直至数据发完。
s.recvfrom() UDP接受数据
s.sendto() UDP发送数据
s.getpeername() 获取当前套接字的远端地址
s.getsockname() 获取当前套接字的地址
s.getsockopt() 获取当前套接字的参数
s.setsockopt() 设置当前套接字的参数
s.close() 关闭套接字

面向锁的套接字函数

s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 获取阻塞套接字操作的超时时间

面向文件的套接字函数

s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件

TCP协议下的socket

0OOY3AKMR({_)5J{3AC)MPY.png

  • 一次交互:

服务器先初始化socket对象,然后绑定端口(bind),对端口进行监听,调用accept()阻塞,等待客户端连接。
客户端初始化socket对象,向服务器发出连接请求,成功的话连接就建立好了。
连接建立后,客户端向服务器请求数据,服务器接受请求并处理后,回应数据给客户端,客户端读取数据后关闭连接,服务器监听到了后也关闭连接(close)。

  1. import socket
  2. # 导入socket包
  3. socket.socket(socket_family, socket_type, protocal = 0)
  4. # 初始化socket对象,socket_family可以是AF_UNIX或AF_INET(本机通信或联机通信),
  5. # socket_type可以是SOCK_STREAM或SOCK_DGRAM(TCP或UDP)。protocal一般不填默认为0.
  6. # 初始化socket对象,获取TCP套接字
  7. tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  8. # 初始化socket对象,获取UDP套接字
  9. udp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  10. # socket模块里有太多属性了,所以我们可以通过from socket import *将模块里的所有属性带到命名空间里
  11. from socket import *
  12. tcp_sock = socket(AF_INET, SOCK_STREAM)

案例一:单个客户端与服务端通信

服务端

  1. import socket
  2. phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  3. # 初始化socket对象 买电话
  4. phone.bind(('127.0.0.1', 8080))
  5. # 绑定(主机地址,端口号) 装电话卡
  6. phone.listen(5)
  7. # 监听端口,同一时刻有5个请求,但是可以有多个连接 开机
  8. conn, client_addr = phone.accept()
  9. # 阻塞等待客户端连接,接收到的是远端套接字和客户端地址 接电话
  10. print(conn, client_addr, sep = '\n')
  11. from_client_data = conn.recv(1024)
  12. # 一次接收最大限制 1024 bytes
  13. print(from_client_data.decode('utf-8'))
  14. conn.send(from_client_data.upper())
  15. conn.close()
  16. # 挂电话
  17. phone.close()
  18. # 关机

客户端

  1. import socket
  2. phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  3. # 初始化socket对象 买电话
  4. phone.connect(('127.0.0.1', 8080))
  5. # 与服务端建立连接 拨号
  6. phone.send('hello'.encode('utf-8'))
  7. # 发送消息hello,编码utf-8
  8. from_server_data = phone.recv(1024)
  9. print(from_server_data)
  10. phone.close()
  11. # 关机

案例二:循环通信

案例一客户端发送一个消息就会断开连接
通过循环来保持连接状态

服务端

  1. import socket
  2. phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  3. phone.bind(('127.0.0.1', 8080))
  4. phone.listen(5)
  5. conn, client_addr = phone.accept()
  6. print(conn, client_addr, sep='\n')
  7. while True:
  8. try:
  9. from_client_data = conn.recv(1024)
  10. print(from_client_data.decode('utf-8'))
  11. conn.send(from_client_data.upper())
  12. except ConnectionResetError:
  13. break
  14. conn.close()
  15. phone.close()

客户端

  1. import socket
  2. phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  3. phone.connect(('127.0.0.1', 8080))
  4. while True:
  5. client_data = input('>>> ')
  6. phone.send(client_data.encode('utf-8'))
  7. from_server_data = phone.recv(1024)
  8. print(from_server_data.decode('utf-8'))
  9. phone.close()