目标:根据socket模块提供的接口函数,进行组合使用完成基于tcp或
者udp的网络编程
套接字:完成上述目标的一种编程手段,编程方案
套接字分类:
流式套接字(SOCK_STREAM):传输层基于tcp协议的套接字编程
方案。
数据报套接字(SOCK_DGRAM):传输层基于udp协议的套接字编程
方案。
底层套接字(SOCK_RAM):访问底层协议的套接字编程。
*面向连接的传输--tcp协议--可靠地--流式套接字
*面向无连接的传输--udp协议--不可靠地--数据报套接字
import socket
1、创建套接字
sock = socket.socket(socket_family = AF_INET,
socket_type = SOCK_STREAM,
proto = 0)
功能:创建套接字
参数:socket_family->选择地址族类型,AF_INET为默认参数,
表示IPV4地址
socket_type:套接字类型:SOCK_STREAM、SOCK_DGRAM
默认为流式套接字
proto:选择子协议类型,通常为0(默认)
返回值:返回套接字对象
2、绑定服务端地址
sock.bind(addr) #单参数,只接收元组
功能:绑定IP地址
参数:元组(ip,port)
ip:
localhost/127.0.0.1 可以被本机访问
本机IP 可以被别人用该IP访问
0.0.0.0 可以被别人用本机IP访问
也可被自己用127.0.0.1访问
3、设置监听套接字
sock.listen(n)
功能:将套接字设置为监听套接字,创建监听队列
参数:n表示监听队列大小
*一个监听套接字可以连接多个客户端套接字,但仍要满足tcp三次握手
依次连接并监听
4、等待处理客户端连接请求
connfd,addr = sock.accept()
功能:阻塞等待处理客户端连接
返回值:connfd 客户端连接套接字
addr 连接的客户端地址
*阻塞函数:程序运行过程中遇到阻塞函数则暂停运行,直到某种阻塞
条件达成再继续运行。
5、消息收发
data = connfd.recv(buffersize)
功能:接受对应客户端消息
参数:一次最多接受多少字节
返回值:接收到的内容
*如果没有消息则会阻塞
n = connfd.send(data)
功能:发送消息给对应客户端
参数:要发送的内容,必须是bytes格式
返回值:返回实际发送消息的大小
6、关闭套接字
sock.close()
功能:关闭套接字
1、创建套接字
* 必须相同类型的套接字才能通信
2、建立连接
sockfd.connect(server_addr)
功能:建立连接
参数:元组,服务端地址
3、消息收发
* 消息收发要和服务端配合,避免两边都出现recv阻塞
4、关闭套接字
#此示例用于编写tcp服务端的操作
#要求接受多个服务端请求,并在一个请求结束后接受另一个请求
from socket import *
sockfd = socket(AF_INET,SOCK_STREAM)
#绑定地址
sockfd.bind(('0.0.0.0',8555))
#开始监听
sockfd.listen(5)
while True:
print('Waiting for connecting....')
connfd,addr = sockfd.accept()
print('Connecting succeed.')
while True:
data = connfd.recv(1024).decode()
if data == 'quit':
connfd.close()
print('Connect break.')
break
print(data)
num = connfd.send(b'Receive your info!')
print('发送的字节数:',num)
#关闭服务端
sockfd.close()
#此示例用来演示tcp_客户端
#向服务端发送数据,quit表示退出
from socket import *
#创建套接字对象
sockfd = socket(AF_INET,SOCK_STREAM)
#连接客户端
server_addr = ('192.168.126.138',8555)
sockfd.connect(server_addr)
while True:
data = input(">>(输入'quit'退出)")
sockfd.send(data.encode())
if data == 'quit':
print('连接断开')
break
data1 = sockfd.recv(1024).decode()
print(data1)
#关闭socket
sockfd.close()
1、监听套接字存在客户端即可发起连接,但是最终连接的处理需要accept进行处理
2、如果连接的另外一端退出,则recv会立即返回空字串不再阻塞。
3、当连接的另一端退出时,再试图send发送就会产生BrokenPipeError
缓冲区作用:协调收发(处理)速度
减少交互次数
send和recv实际上是和缓冲区进行交互,发送缓冲区满时就无法发送,接收缓冲区
满时recv才阻塞,发送字节数多于接收缓冲区容量时(recv中的字节参数)会分多
次发送
TCP粘包
产生原因:
TCP套接字以字节流方式传输,没有消息边界
发送和接收并不能保证每次发送都及时的被接收
影响:
如果每次发送内容表达一个独立的含义此时可能需要处理粘包防止产生歧义
处理方法:
1、每次发送的消息添加结尾标志(人为增加消息边界)
2、发送数据结构体
3、协调收发速度,每次发送后都预留接收时间
基于UDP套接字的服务端
无连接的UDP不需要像面向连接的TCP监听(listen)和处理连接(accept)
1、创建数据报套接字
sockfd = socket(AF_INET,SOCK_DGRAM) #不能使用默认的流式套接字
2、绑定地址
sockfd.bind(address)
3、消息的收发
data,addr = sockfd.recvfrom(buffsize)
功能:接收UDP消息
参数:每次最多接收多大字节的消息
返回值:data->接收到的数据
addr->消息发送端的地址
sockfd.sendto(data,addr)
功能:发送udp消息
参数:data->发送的消息 bytes格式
addr->目标地址
返回值:发送的字节数
4、关闭套接字
sockfd.close()
1、创建套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
2、收发消息
sockfd.recvfrom/sendto()
3、关闭套接字
sockfd.close()
4、补充函数
sendall(data)
功能:发送tcp消息
参数:要发送的内容,bytes格式
返回值:成功返回None失败产生异常
sys.argv属性
获取命令行参数,得到一个列表,以空格分隔
命令本身是argv[0]
后面的参数从[1]开始,默认以空格分隔
使用引号引起来的内容算作一个整体
命令行参数都以字符串放入列表
tip:
import sys
IP_addr = sys.argv[1]
PORT = int(sys.argv[2])
addr = (IP_addr,PORT)
$python3 test.py 127.0.0.1 9999
在程序的第一行添加
#!/usr/bin/env python3
添加程序的执行权限
chomod 755 file.py
修改后即可通过./file.py 执行程序
./是指执行路径,若设置环境变量可省略
1、流式套接字使用字节流的方式传输,数据报套接字以数据
报形式传输数据
2、tcp会有粘包现象,udp有消息边界不会形成粘包
3、tcp可以保障数据传输完整性,udp不保证
4、tcp需要进行listen accept操作,udp不需要
5、tcp收发消息使用新的套接字,recv sent。udp使用
recvfrom,sendto