人工智能(PythonNet)—— TCP服务器、UDP服务器基础编程


一、socket套接字编程

        目的 : 通过编程语言提供的函数接口进行组合,更简单的完成基于tcp和udp通信的网络编程
        套接字:进行网络通信的一种手段 socket

    1、套接字的分类

                流式套接字(SOCK_STREAM): 传输层基于tcp的协议进行通信
                数据报套接字(SOCK_DGRAM): 传输层基于udp的协议进行通信
                底层套接字(SOCK_RAM):访问底层协议的套接字

    2、socket功能函数(s表示一个套接字对象)

                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):获取套接字选项的值

    3、tcp流式套接字、udp数据报套接字的区别

            a、流式套接字采用字节流的方式传输数据,而数据报套接字以数据报形式传输
            b、tcp会产生粘包现象,udp消息是有边界的不会粘包
            c、tcp传输是建立在连接的基础上,保证传输的可靠性,而udp一次接受一个数据报,不保证完整性
            d、tcp需要依赖listen accept建立连接,udp不用
            e、tcp收发消息使用recv、send; udp收发消息使用recvfrom、sendto

二、TCP服务

        套接字导入
                from socket  import *

    1、TCP服务器

            socket()  -->  bind()  --> listen()  --> accept()  --> recv()/send()  -->  close()

        1)创建套接字

                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 (表示选择子协议)
                返回值:返回一个创建的套接字对象

        2)绑定IP端口

                sockfd.bind()

                功能 : 绑定IP和端口
                参数: 二元元组,第一项为ip 第二项为端口号
                                                 e.g:("192.168.1.2",8888)

        3)将套接字设置为可监听

                sockfd.listen(n)

                功能:将套接字设置为监听套接字,并创建监听队列
                参数: 监听对象的大小   n为大于0 的正整数

        4)等待客户端的连接

                connfd,addr = sockfd.accept()

                功能:   阻塞等待客户端连接
                返回值 :connfd  :返回的一个新的套接字,用于和指定客户端通信
                                 addr    :连接的客户端的地址

                    * 阻塞函数: 程序运行到阻塞函数位置,如果某种预期条件没有达成则暂停继续运行,直到条件达成后再继续运行

        5)消息收发

                data = connfd.recv(buffersize)

                功能 : 接收消息
                参数 : 每次最多接收的消息大小  bytes
                返回值 : 返回接收到的内容    .decode()

                n = connfd.send(data)

                功能 : 发送消息
                参数 : 要发送的内容  必须是bytes格式   .encode()
                返回值 : 实际发送的字节数

        6)关闭套接字

                sockfd.close()

                功能 : 关闭套接字,tcp连接断开
        注: telnet  ip   port   用于连接tcp服务端

    2、tcp客户端

            socket() --> connect()  --> recv()/send()  -->  close()
        1)创建套接字

                socket()
                * 通信的两端套接字类型相同

        2)发起连接

                connect()
                功能 : 发起连接请求
                参数 : 元组,服务器端的地址

        3)发收消息

                send  recv
                * 两端收发需要配合

        4)关闭套接字

                close()

    3、示例

        a、TCP服务器
#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()
        b、TCP客户端
#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()

    4、补充

        a、recv()特征

                1)如果连接端断开,recv会立即结束阻塞返回空字符串
                2)当接收缓冲区为空时会阻塞
                3)如果recv一次接受不完缓冲区内容,下次会继续接受,确保数据不丢失

        b、send()特性

                1)如果另一端不存在还试图使用send进行发送则会产生BrokenPipeError异常
                2)当发送缓冲区满时会阻塞

 注:网络收发缓冲区
        *缓冲区的功能 : 协调读写速度,减少和磁盘交互
        recv和send实际上是从缓冲区内获取内容,和向缓冲区发送内容

        c、tcp粘包

        产生原因:tcp传输采用字节流的方式,消息之间没有边界,如果发送和接受速度不匹配,会造成多次发送的内容被一次接受,形成意义上的误解即粘包
        产生条件 : 当使用send快速的连续发送极有可能产生粘包。
        影响: 如果每次发送的内容代表一个独立的意思,此时产生粘包需要处理。但是如果多次发送的内容本身就是一个连续的整体,此时就不需要处理。

        解决方法:
                1) 每次发送后加一个结尾标志,接收端通过标志进行判断
                2)发送一个数据结构
                3)每次发送中间有一个短暂的延迟

三、UDP服务

        套接字导入
                from socket  import *

    1、UDP服务端

        socket() -->  bind()  --> recvfrom()/sendto()  -->  close()

        1)创建套接字 ---》 数据报套接字
                sockfd = socket(AF_INET,SOCK_DGRAM)
        2)绑定服务端地址
                sockfd.bind()
        3)消息的收发
                data,addr = recvfrom(buffersize)
                        功能 : 接受udp消息
                        参数 : 每次最多接收消息的大小
                     返回值 : data  接收到的消息
                                     addr  消息发送者的地址

                sendto(data,addr)
                        功能: udp发送消息
                        参数: data要发送的消息   bytes
                                   addr 目标地址
                   返回值 : 发送的字节
        4)关闭套接字
                sockfd.close()

    2、udp客户端

        socket() --recvfrom()/sendto()  -->  close()

        1)创建套接字 ---》 数据报套接字
                sockfd = socket(AF_INET,SOCK_DGRAM)
        3)消息的收发
                data,addr = recvfrom(buffersize)
                sendto(data,addr)
        4)关闭套接字
                sockfd.close()

    3、示例一

        a、udp服务器
# 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()
        b、udp客户端
# 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()

    4、示例二

        a、广播服务器
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()
        b、广播客户端
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)—— 目录汇总



版权声明:本文为qq_27297393原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。