python基础之socket编程2:UDP

一、基于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)
udp服务端
#_*_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()
udp客户端1
#_*_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()
udp客户端2

服务端运行结果

客户端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消失了,服务端要么报异常要么一直在收空,在收发消息的死循环中
 

猜你喜欢

转载自www.cnblogs.com/Josie-chen/p/8931314.html