python3–socket编程(udp协议)

2024年04月20日 Python教程 Python51

基于UDP协议的socket

udp是无连接的,启动服务之后可以直接接受消息,不需要提前建立连接,UDP必须是server端先接受消息

简单版

server端代码

<span class="hljs-keyword">import</span> socket

<span class="hljs-comment"># 创建一个服务器的套接字基于udp,type=socket.SOCK_DGRAM表示使用udp协议</span>
udp_sk = socket.socket(type=socket.SOCK_DGRAM)
udp_sk.bind((<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9555</span>))  <span class="hljs-comment"># 绑定服务器的ip和端口的套接字</span>

<span class="hljs-comment"># udp协议不用建立连接</span>
msg, addr = udp_sk.recvfrom(<span class="hljs-number">1024</span>)  <span class="hljs-comment"># 接收1024字节的消息 msg表示内容,addr表示ip和端口</span>
print(msg.decode(<span class="hljs-string">'utf-8'</span>))
udp_sk.sendto(<span class="hljs-string">'hello'</span>.encode(<span class="hljs-string">'utf-8'</span>), addr)  <span class="hljs-comment">#发送消息,需写入对方的ip和端口</span>
udp_sk.close()

client端代码

<span class="hljs-keyword">import</span> socket

ip_port = (<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9555</span>)  <span class="hljs-comment"># 服务器的ip与端口</span>

<span class="hljs-comment"># 创建一个服务器的套接字,基于udp协议 type=socket.SOCK_DGRAM</span>
udp_sk = socket.socket(type=socket.SOCK_DGRAM)

<span class="hljs-comment"># 发送消息并编码成utf-8,需要传ip和端口</span>
udp_sk.sendto(<span class="hljs-string">'你好吗'</span>.encode(<span class="hljs-string">'utf-8'</span>), ip_port)

<span class="hljs-comment"># 接收1024字节的消息 加 ip+端口</span>
back_msg, addr = udp_sk.recvfrom(<span class="hljs-number">1024</span>)
print(back_msg.decode(<span class="hljs-string">'utf-8'</span>), addr)
udp_sk.close()

先运行server,再运行client

server执行结果

你好吗

client执行结果

hello (‘127.0.0.1’, 9555)

怎么改成手动输入,多人聊天?

server端代码

<span class="hljs-keyword">import</span> socket

<span class="hljs-comment"># 创建一个服务器的套接字基于udp,type=socket.SOCK_DGRAM表示使用udp协议</span>
udp_sk = socket.socket(type=socket.SOCK_DGRAM)

udp_sk.bind((<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9555</span>))   <span class="hljs-comment"># 绑定服务器的ip和端口的套接字</span>
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    <span class="hljs-comment"># udp协议不用建立连接</span>
    msg, addr = udp_sk.recvfrom(<span class="hljs-number">1024</span>)  <span class="hljs-comment"># 接收1024字节的消息 msg表示内容,addr表示ip和端口</span>
    print(msg.decode(<span class="hljs-string">'utf-8'</span>))
    inp = input(<span class="hljs-string">'>>>'</span>).strip().encode(<span class="hljs-string">'utf-8'</span>)
    udp_sk.sendto(inp, addr)  <span class="hljs-comment"># 发送消息,需写入对方的ip和端口</span>

udp_sk.close()

client端代码

<span class="hljs-keyword">import</span> socket

ip_port = (<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9555</span>)  <span class="hljs-comment"># 服务器的ip与端口</span>

<span class="hljs-comment"># 创建一个服务器的套接字,基于udp协议 type=socket.SOCK_DGRAM</span>
udp_sk = socket.socket(type=socket.SOCK_DGRAM)

<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    <span class="hljs-comment"># 发送消息,需要传ip和端口</span>
    inp = input(<span class="hljs-string">'>>>'</span>).strip()
    <span class="hljs-keyword">if</span> inp == <span class="hljs-string">'q'</span>:  <span class="hljs-comment"># 判断当内容为q的时候,跳出循环,退出程序</span>
        <span class="hljs-keyword">break</span>
    udp_sk.sendto(inp.encode(<span class="hljs-string">'utf-8'</span>), ip_port)

    <span class="hljs-comment"># 接收1024字节的消息 加 ip+端口</span>
    back_msg, addr = udp_sk.recvfrom(<span class="hljs-number">1024</span>)
    print(back_msg.decode(<span class="hljs-string">'utf-8'</span>))
udp_sk.close()

先运行server,再运行client两次(模拟多人),运行结果

socket基于UDP协议,如果是2个客户端,都向server发送消息,那么server对于client,还是有优先顺序的(谁发送的数据,被server接受到,那么就先回复谁(网络延迟,带宽…的影响,并不是先发送就先收到))

socket(基于UDP协议)进阶聊天版–加上和不同人名通信(不同颜色)

选运行server,在运行client两次(模拟多个用户)

server端代码

<span class="hljs-keyword">import</span> socket

<span class="hljs-comment"># 创建一个服务器的套接字基于udp,type=socket.SOCK_DGRAM表示使用udp协议</span>
udp_sk = socket.socket(type=socket.SOCK_DGRAM)
udp_sk.bind((<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9555</span>))   <span class="hljs-comment"># 绑定服务器的ip和端口的套接字</span>
lst = {<span class="hljs-string">'sam'</span>: <span class="hljs-string">'\033[1;31m'</span>, <span class="hljs-string">'tom'</span>: <span class="hljs-string">'\033[1;34m'</span>}  <span class="hljs-comment"># 对不同人设置不同颜色</span>

<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    <span class="hljs-comment"># udp协议不用建立连接</span>
    msg, addr = udp_sk.recvfrom(<span class="hljs-number">1024</span>)  <span class="hljs-comment"># 接收1024字节的消息 msg表示内容,addr表示ip和端口</span>
    name, mesg = msg.decode(<span class="hljs-string">'utf-8'</span>).split(<span class="hljs-string">':'</span>)
    color = lst.get(name.strip(), <span class="hljs-string">''</span>)
    print(<span class="hljs-string">'{}{}\033[0m'</span>.format(color, msg.decode(<span class="hljs-string">'utf-8'</span>)))
    inp = input(<span class="hljs-string">'>>>'</span>).strip().encode(<span class="hljs-string">'utf-8'</span>)
    udp_sk.sendto(inp, addr)  <span class="hljs-comment"># 发送消息,需写入对方的ip和端口</span>

udp_sk.close()

client端代码

<span class="hljs-keyword">import</span> socket

ip_port = (<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9555</span>)  <span class="hljs-comment"># 服务器的ip与端口</span>

<span class="hljs-comment"># 创建一个服务器的套接字,基于udp协议 type=socket.SOCK_DGRAM</span>
udp_sk = socket.socket(type=socket.SOCK_DGRAM)
name = input(<span class="hljs-string">'>>>'</span>).strip()
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    <span class="hljs-comment"># 发送消息,需要传ip和端口</span>
    inp = input(<span class="hljs-string">'>>>'</span>).strip()
    <span class="hljs-keyword">if</span> inp == <span class="hljs-string">'q'</span>:  <span class="hljs-comment"># 判断当内容为q的时候,跳出循环,退出程序</span>
        <span class="hljs-keyword">break</span>
    udp_sk.sendto(<span class="hljs-string">'{} : {}'</span>.format(name, inp).encode(<span class="hljs-string">'utf-8'</span>), ip_port)

    <span class="hljs-comment"># 接收1024字节的消息 加 ip+端口</span>
    back_msg, addr = udp_sk.recvfrom(<span class="hljs-number">1024</span>)
    print(back_msg.decode(<span class="hljs-string">'utf-8'</span>))
udp_sk.close()

运行结果

每次发消息需要encode,接收消息需要decode,这样很麻烦?怎么做可以省去这些步骤?,这个时候可以自定义一个类,继承了socket类,能够实现高可用,定制需求

创建一个mysocket.py文件,内容如下

<span class="hljs-keyword">from</span> socket <span class="hljs-keyword">import</span> *   <span class="hljs-comment">#从socket模块中导入所有方法</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Mysocket</span><span class="hljs-params">(socket)</span>:</span>  <span class="hljs-comment"># 继承socket类</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self, coding=<span class="hljs-string">'utf-8'</span>)</span>:</span>
        self.coding = coding  <span class="hljs-comment"># 使用默认参数,设置字符编码 utf-8</span>
        super().__init__(type=SOCK_DGRAM)  <span class="hljs-comment"># 执行父类的__init__方法</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_recv</span><span class="hljs-params">(self, num)</span>:</span>
        msg, addr = self.recvfrom(num)
        <span class="hljs-keyword">return</span> msg.decode(self.coding), addr  <span class="hljs-comment"># 接收消息这里解码</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_send</span><span class="hljs-params">(self, msg, addr)</span>:</span>
        <span class="hljs-keyword">return</span> self.sendto(msg.encode(self.coding), addr)  <span class="hljs-comment"># 发送消息这么编码</span>

server端代码

<span class="hljs-keyword">from</span> mysocket <span class="hljs-keyword">import</span> Mysocket

sk = Mysocket()
sk.bind((<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9090</span>))
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    msg, client_addr = sk.my_recv(<span class="hljs-number">1024</span>)  <span class="hljs-comment"># udp协议不用建立连接</span>
    print(msg)
    inp = input(<span class="hljs-string">'>>>'</span>).strip()
    sk.my_send(inp, client_addr)

sk.close()

client端代码

<span class="hljs-keyword">from</span> mysocket <span class="hljs-keyword">import</span> Mysocket

sk = Mysocket()
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    inp = input(<span class="hljs-string">'>>>'</span>).strip()
    <span class="hljs-keyword">if</span> inp == <span class="hljs-string">'q'</span>:<span class="hljs-keyword">break</span>
    sk.my_send(inp, (<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9090</span>))
    msg, addr = sk.my_recv(<span class="hljs-number">1024</span>)
    <span class="hljs-keyword">if</span> msg == <span class="hljs-string">'q'</span>:<span class="hljs-keyword">break</span>
    print(msg)
sk.close()

先运行server,在运行client,执行结果为

时间同步服务,基于udp协议完成的

假如有一个标准时间的server服务端,client可以从server获取标准时间。

server端代码

<span class="hljs-keyword">import</span> socket
<span class="hljs-keyword">import</span> time

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind((<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9090</span>))
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    msg, addr = sk.recvfrom(<span class="hljs-number">1024</span>)
    sk.sendto(time.strftime(<span class="hljs-string">'%Y-%m-%d %H:%M:%S'</span>).encode(<span class="hljs-string">'utf-8'</span>), addr)  <span class="hljs-comment"># 发送一个格式化时间给client</span>
sk.close()

client端代码

<span class="hljs-keyword">import</span> time

sk = socket.socket(type=socket.SOCK_DGRAM)
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    sk.sendto(<span class="hljs-string">'time'</span>.encode(<span class="hljs-string">'utf-8'</span>),(<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-number">9090</span>))
    ret,addr = sk.recvfrom(<span class="hljs-number">1024</span>)
    print(ret.decode(<span class="hljs-string">'utf-8'</span>))
    time.sleep(<span class="hljs-number">2</span>)
sk.close()

先运行server在运行client,效果为

查看TCP和UDP的套接字

socket参数的详解

socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)

创建socket对象的参数说明:

family 地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。 (AF_UNIX 域实际上是使用本地 socket 文件来通信)
type 套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。 SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。 SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。
proto 协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。
fileno 如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。 与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。 这可能有助于使用socket.close()关闭一个独立的插座。

本文链接:http://so.lmcjl.com/news/2642/

展开阅读全文