一、基于UDP的套接字
udp是无链接的,先启动哪一端都不会报错。没有连接池backlog,不需要listen了,也不需要连接循环
udp服务端
1 ss = socket() #创建一个服务器的套接字 2 ss.bind() #绑定服务器套接字(IP地址和端口)
#这里没有listen了,listen实在TCP中挂起半链接的 3 inf_loop: #服务器无限循环 4 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送) 5 ss.close() # 关闭服务器套接字
udp客户端
cs = socket() # 创建客户套接字 comm_loop: # 通讯循环 cs.sendto()/cs.recvfrom() # 对话(发送/接收) cs.close() # 关闭客户套接字
udp套接字简单示例
# UDP服务端 from socket import * ip_port=('127.0.0.1',8080) buffer_size=1024 udp_server=socket(AF_INET,SOCK_DGRAM) #数据报式的套接字 udp_server.bind(ip_port) while True: data,addr=udp_server.recvfrom(buffer_size) #addr为给本服务端发消息的客户端的IP地址和端口号 print(data) udp_server.sendto(data.upper(),addr)
#回消息给客户端,addr为相应客户端地址和端口号
# UDP客户端 from socket import * ip_port=('127.0.0.1',8080) buffer_size=1024 udp_client=socket(AF_INET,SOCK_DGRAM) #数据报:UDP协议 while True: msg=input('>>: ').strip() udp_client.sendto(msg.encode('utf-8'),ip_port) #由于UDP中没有连接,所以每次发的时候都要指定发给哪个端口 #sento中参数表:发送的消息,服务端的IP地址和端口。 data,addr=udp_client.recvfrom(buffer_size) # print(data.decode('utf-8')) print(data)
注意:
1、如果客户端用户输入空,则成功发送给了服务端,服务端打印:b'' 并且服务端将收的空按程序写的将空变大写(还是一个空),返回给了客户端:b''。
对比TCP在发空的时候,缓冲区空就不能收发出去。即recv在缓存区中如果为空就直接阻塞住。
而UDP中空的也能被收到。recvfrom()在自己这端的缓冲区为空就接收一个空。
2、多个客户端与服务端通信时。
在我们已经实现的TCP中,服务端和客户端还不能实现并发的效果:在我们的程序当中,每次只能服务一个人,因为只有两个循环,只要外层循环不断,那一直就在内层循环中,这意味着一直在跟同一个客户端交互,直到与这个客户端断开连接后,才能与下一个客户端建立新的连接,然后重复。
而UDP实现了并发,因为UDP不用建立链接。
qq聊天(由于udp无连接,所以可以同时多个客户端去跟服务端通信)
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',8081) udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #买手机 udp_server_sock.bind(ip_port) while True: qq_msg,addr=udp_server_sock.recvfrom(1024) print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8'))) back_msg=input('回复消息: ').strip() udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) qq_name_dic={ '狗哥alex':('127.0.0.1',8081), '瞎驴':('127.0.0.1',8081), '一棵树':('127.0.0.1',8081), '武大郎':('127.0.0.1',8081), } while True: qq_name=input('请选择聊天对象: ').strip() while True: msg=input('请输入消息,回车发送: ').strip() if msg == 'quit':break if not msg or not qq_name or qq_name not in qq_name_dic:continue udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name]) back_msg,addr=udp_client_socket.recvfrom(BUFSIZE) print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8'))) udp_client_socket.close()
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) qq_name_dic={ '狗哥alex':('127.0.0.1',8081), '瞎驴':('127.0.0.1',8081), '一棵树':('127.0.0.1',8081), '武大郎':('127.0.0.1',8081), } while True: qq_name=input('请选择聊天对象: ').strip() while True: msg=input('请输入消息,回车发送: ').strip() if msg == 'quit':break if not msg or not qq_name or qq_name not in qq_name_dic:continue udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name]) back_msg,addr=udp_client_socket.recvfrom(BUFSIZE) print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8'))) udp_client_socket.close()
服务端运行结果
客户端1运行结果
客户端2运行结果
时间服务器ntp:用UDP来实现
#ntp服务端 from socket import * import time #加入时间模块 ip_port=('127.0.0.1',8080) buffer_size=1024 udp_server=socket(AF_INET,SOCK_DGRAM) #数据报 udp_server.bind(ip_port) while True: data,addr=udp_server.recvfrom(buffer_size) print(data) if not data: # 如果用户输入为空(False),前加上not。 #表示data为空就执行,表示data为空就执行, 则返回默认格式 fmt='%Y-%m-%d %X' #年-月-日 时:分:秒 else: fmt=data.decode('utf-8') #若用户自己有指定格式,则将指定格式解码作为时间格式 #自定义格式:%m-%d-%Y 月-日-年 back_time=time.strftime(fmt) #设定好格式为fmt udp_server.sendto(back_time.encode('utf-8'),addr) #将字符串形式的数据编码发出去 # PS:要是想要发送的数据为数字类型,则需要先将数字转化为字符串格式之后在编码传送出去。 udp_server.close()
TCP应用:基于TCP实现远程执行命令:
1、subprocess模块
(1) 用shell(命令解释器工具)来解释你输入的命令:两个程序之间直接通信是不行的,需要介质:利用管道PIPE来实现两个程序之间的通信。
在cmd中如果直接输入:
第一步:res=subprocess.Popen('dir', shell=True),默认会在屏幕上直接打印出命令的运行结果。即此时命令直接subprocess.Popen这个模块调用自己的命令并在后台执行此命令了,并将执行的结果直接传给屏幕(另外一个程序了)这是两个程序之间的通信。
第二步:res
<subprocess.Popen object at 0x0000000000110B128>
利用res得不到结果,只能得到一个subprocess.Popen的对象
(2) 自行控制管道,stout为程序运行正确的标准输出
stout=subprocess.PIPE,将标准输出结果交给管道,此时运行得到的数据就停在管道当中了,屏幕就不会直接输出了。
res.stdout.read() 读取管道内容,读完之后管道就为空了。如果再执行一遍,得到结果为空。
stdin=subprocess.PIPE将标准输入结果交给管道
stderr=subprocess.PIPE将标准错误输出结果(运行如果出错的时候)交给管道
这三个扔进的是三个不同的管道
2、代码实现:
#服务端 from socket import * import subprocess ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_server=socket(AF_INET,SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: #建立链接大循环 conn,addr=tcp_server.accept() print('新的客户端链接',addr) while True: #收到的为命令。 #服务端基于之前三次握手建立起来的链接,会在这里等待recv #此时若客户端退出, #如果不是输入quit命令终止,而是客户端直接点结束程序运行按钮来断开连接, #用try-except来解决这种非正常关闭情况下的异常处理 try: cmd=conn.recv(buffer_size) #字节格式 #当客户端选择了quit,客户端退出循环执行了close()命令将服务端的conn直接断开时, #此时会导致服务端频繁收到空,此时用if来处理 if not cmd:break print('收到客户端的命令',cmd) #执行命令,得到命令的运行结果cmd_res #用shell(命令解释器)来处理, #两个程序之间直接通信是不行的,需要介质:利用管道PIPE来实现两个程序之间的通信 #stout为程序运行正确的标准输出,stdin为程序运行正确的标准输入,stderr为错误输出结果 res=subprocess.Popen(cmd.decode('utf-8'),shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE) #这三个扔进了三个不同的管道 err=res.stderr.read() if err: #先判断是否有错误 cmd_res=err else: cmd_res=res.stdout.read() #读取正确的管道内容 #发 if not cmd_res: cmd_res='执行成功'.encode('gbk') #windows下系统默认编码为gbk conn.send(cmd_res) except Exception as e: print(e) break #通信循环(内层的while)终止,进入下一次最外层的while循环,等待建立新的连接 #conn.close()
# 客户端 from socket import * ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_client=socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd=input('>>: ').strip() if not cmd:continue #发空:如果客户端收了空,那就会陷入死循环 if cmd == 'quit':break #如果用户输入了退出命令,此时直接退出while循环 tcp_client.send(cmd.encode('utf-8')) cmd_res=tcp_client.recv(buffer_size) print('命令的执行结果是 ',cmd_res.decode('gbk')) tcp_client.close() #导致服务端conn消失了,服务端要么报异常要么一直在收空,在收发消息的死循环中