目的 : 通过编程语言提供的函数接口进行组合,更简单的完成基于tcp和udp通信的网络编程
套接字:进行网络通信的一种手段 socket
流式套接字(SOCK_STREAM): 传输层基于tcp的协议进行通信
数据报套接字(SOCK_DGRAM): 传输层基于udp的协议进行通信
底层套接字(SOCK_RAM):访问底层协议的套接字
s.type : 获取套接字类型
s.family : 获取地址族类型
s.fileno(): 获取套接字的文件描述符
s.getsockname(): 获取套接字绑定地址
s.getpeername(): 获取连接套接字另一端的地址
s.setsockopt(level,optname,value)
功能:设置套接字选项 丰富修改原有套接字功能
参数: level : 获取选项的类型
optname : 每个选项类型中的子选项
value : 为选项设置值
#设置端口可重用
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#设置套接字可以发送接受广播
s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
s.getsockopt(level,optname):获取套接字选项的值
a、流式套接字采用字节流的方式传输数据,而数据报套接字以数据报形式传输
b、tcp会产生粘包现象,udp消息是有边界的不会粘包
c、tcp传输是建立在连接的基础上,保证传输的可靠性,而udp一次接受一个数据报,不保证完整性
d、tcp需要依赖listen accept建立连接,udp不用
e、tcp收发消息使用recv、send; udp收发消息使用recvfrom、sendto
socket() --> bind() --> listen() --> accept() --> recv()/send() --> close()
sockfd = socket(socket_family = AF_INET,socket_type = SOCK_STREAM,proto = 0)
功能:创建一个套接字
参数: socket_family 选择地址族类型 AF_INET 表示IPv4
socket_type 选择套接字类型 SOCK_STREAM 流式
SOCK_DGRAM 数据报
proto 通常为0 (表示选择子协议)
返回值:返回一个创建的套接字对象
sockfd.bind()
功能 : 绑定IP和端口
参数: 二元元组,第一项为ip 第二项为端口号
e.g:("192.168.1.2",8888)
sockfd.listen(n)
功能:将套接字设置为监听套接字,并创建监听队列
参数: 监听对象的大小 n为大于0 的正整数
connfd,addr = sockfd.accept()
功能: 阻塞等待客户端连接
返回值 :connfd :返回的一个新的套接字,用于和指定客户端通信
addr :连接的客户端的地址
* 阻塞函数: 程序运行到阻塞函数位置,如果某种预期条件没有达成则暂停继续运行,直到条件达成后再继续运行
data = connfd.recv(buffersize)
功能 : 接收消息
参数 : 每次最多接收的消息大小 bytes
返回值 : 返回接收到的内容 .decode()
n = connfd.send(data)
功能 : 发送消息
参数 : 要发送的内容 必须是bytes格式 .encode()
返回值 : 实际发送的字节数
sockfd.close()
功能 : 关闭套接字,tcp连接断开
注: telnet ip port 用于连接tcp服务端
socket()
* 通信的两端套接字类型相同
connect()
功能 : 发起连接请求
参数 : 元组,服务器端的地址
send recv
* 两端收发需要配合
close()
#tcp_server.py
from socket import *
#创建tcp套接字
sockfd = socket(AF_INET,SOCK_STREAM)
#绑定IP和端口
sockfd.bind(("127.0.0.1",8888))
#设置监听
sockfd.listen(5)
#等待客户端链接
print("Waiting for connect....")
connfd,addr = sockfd.accept()
print("Connect from",addr)
#接收
data = connfd.recv(1024)
print("receive message {} from {}".format(data.decode(), addr))
#发送
n = connfd.send(b"Receive your message\n")
print("Send %d bytes data"%n)
#关闭套接字
connfd.close()
sockfd.close()
#tcp_client.py
from socket import *
#创建套接字 tcp 默认参数即可
sockfd = socket()
#发起链接请求
sockfd.connect(('127.0.0.1',8888))
data = input("发送>>")
#发送消息 bytes格式
sockfd.send(data.encode())
data = sockfd.recv(1024).decode()
print(data)
#关闭套接字
sockfd.close()
1)如果连接端断开,recv会立即结束阻塞返回空字符串
2)当接收缓冲区为空时会阻塞
3)如果recv一次接受不完缓冲区内容,下次会继续接受,确保数据不丢失
1)如果另一端不存在还试图使用send进行发送则会产生BrokenPipeError异常
2)当发送缓冲区满时会阻塞
注:网络收发缓冲区
*缓冲区的功能 : 协调读写速度,减少和磁盘交互
recv和send实际上是从缓冲区内获取内容,和向缓冲区发送内容
产生原因:tcp传输采用字节流的方式,消息之间没有边界,如果发送和接受速度不匹配,会造成多次发送的内容被一次接受,形成意义上的误解即粘包
产生条件 : 当使用send快速的连续发送极有可能产生粘包。
影响: 如果每次发送的内容代表一个独立的意思,此时产生粘包需要处理。但是如果多次发送的内容本身就是一个连续的整体,此时就不需要处理。
解决方法:
1) 每次发送后加一个结尾标志,接收端通过标志进行判断
2)发送一个数据结构
3)每次发送中间有一个短暂的延迟
套接字导入
from socket import *
socket() --> bind() --> recvfrom()/sendto() --> close()
1)创建套接字 ---》 数据报套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
2)绑定服务端地址
sockfd.bind()
3)消息的收发
data,addr = recvfrom(buffersize)
功能 : 接受udp消息
参数 : 每次最多接收消息的大小
返回值 : data 接收到的消息
addr 消息发送者的地址
1)创建套接字 ---》 数据报套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
3)消息的收发
data,addr = recvfrom(buffersize)
sendto(data,addr)
4)关闭套接字
sockfd.close()
# udp_server.py
from socket import *
import sys
# print(sys.argv)
if len(sys.argv) == 2:
pass
else:
sys.exit(1)
sockfd = socket(AF_INET, SOCK_DGRAM)
sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sockfd.bind(("localhost", int(sys.argv[1])))
data, addr = sockfd.recvfrom(1024)
print("来自{}的数据:".format(addr, data.decode()))
n = sockfd.sendto("收到".encode(), addr)
print("发送了%d个数据"%n)
sockfd.close()
# udp_client.py
from socket import *
import sys
# print(sys.argv)
if len(sys.argv) == 2:
pass
else:
sys.exit(1)
sockfd = socket(AF_INET, SOCK_DGRAM)
n = sockfd.sendto("收到".encode(),("localhost", int(sys.argv[1])))
print("发送了%d个数据"%n)
data, addr = sockfd.recvfrom(1024)
print("来自{}的数据:".format(addr, data.decode()))
sockfd.close()
from socket import *
from time import sleep
#设置广播地址
dest = ('xxx.xxx.x.255',9999)
s = socket(AF_INET,SOCK_DGRAM)
#设置套接字可以发送接受广播
s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
while True:
sleep(2)
s.sendto("加油,.....!!".encode(),dest)
s.close()
from socket import *
#创建套接字
s = socket(AF_INET,SOCK_DGRAM)
#设置可以接收广播
s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
#绑定端口
s.bind(('',9999))
while True:
try:
msg,addr = s.recvfrom(1024)
print("从{}获取信息:{}".\
format(addr,msg.decode()))
except KeyboardInterrupt:
print("接收消息结束")
break
except Exception as e:
print(e)
s.close()
人工智能(PythonNet)—— 目录汇总