python网络编程socket编程(TCP、UDP客户端服务器)

摘录 python核心编程

使用socket()模块函数创建套接字——通信端点

>>> from socket import *
>>> tcpSock = socket(AF_INET,SOCK_STREAM)
>>> udpSock = socket(AF_INET,SOCK_DGRAM)

其中,AF_INET表示使用的是IPv4协议,SOCK_STREAM表示的面向连接的TCP协议,SOCK_DGRAM表示面向无连接的UDP协议。

在创建TCP和UDP客户端与服务器前,先看看socket模块的属性以及套接字对象的方法

socket模块的属性:

属性名 描述
数据属性
AF_UNIX、AF_INET、AF_INET6、AF_NETLINK、AF_TIPC python支持的套接字地址家族
SO_STREAM、SO_DGRAM 套接字类型(TCP=流,UDP=数据报)
has_ipv6 表示是否支持IPv6的布尔标志
异常
error 套接字相关错误
herror 主机和地址相关错误
gaierror 地址相关错误
timeout 超时时间
函数
socket() 以给定的地址家族、套接字类型和协议类型(可选)创建一个套接字对象
socketpair() 以给定的地址家族、套接字类型和协议类型(可选)创建一对套接字对象
create_connection() 常规函数,它接收一个地址(主机、端口号)对,返回套接字对象
fromfd() 以一个打开的文件描述符创建一个套接字对象
ssl() 通过套接字启动一个安全套接字连接,不执行证书验证
getaddrinfo() 获取一个五元组序列形式的地址信息
getnameinfo() 给定一个套接字地址,返回二元组(主机名、端口号)
getfqdn() 返回完整的域名
gethostname() 返回当前主机名
gethostbyname() 将一个主机名映射到它的IP地址
gethostbyname_ex() gethostname()的扩展,返回主机名、别名主机集合和IP地址列表
gethostbyaddr() 将一个IP地址映射到DNS信息;返回和gethostbyname_ex()相同的三元组
getprotobyname() 将一个协议名映射到一个数字
getservbyname()/getservbyport() 将一个服务名映射到一个端口号,或者反过来;任何一个函数,协议名都是可选的
ntohl()/ntohs() 将来自网络的整数转换为主机字节顺序
htonl()/htons() 将来自主机的整数转换为网络字节顺序
inet_aton()/inet_ntoa() 将IP地址八进制字符串转换为32位包格式,或者反过来(仅用于IPv4地址)
inet_pton()/inet_ntop() 将IP地址字符串转换为打包的二进制格式,或者反过来(适用于IPv4和IPv6)
getdefaulttimeout()/setdefaulttimeout() 以秒为单位,获得/设置默认套接字超时时间

 

套接字对象方法和属性:

名称 描述
服务器套接字特有方法
s.bind() 将地址(主机名、端口号)绑定到套接字上
s.listen 设置并启动TCP监听器
s.accept() 被动接受TCP客户端连接,一直等到直到连接到达(阻塞)
客户端套接字特有方法
s.connect() 主动发起TCP服务器链接
s.connect_ex() connect()的扩展,此时会以错误码的形式返回问题,而不是抛出一个异常
普通的套接字方法
s.recv() 接受TCP消息
s.recv_into() 接受TCP消息到指定的缓冲区
s.send() 发送TCP消息
s.sendall() 完整的发送TCP消息
s.recvfrom() 接受UDP消息
s.recvfrom_into() 接受UDP消息到指定的缓冲区
s.sendto() 发送UDP消息
s.getpeername() 连接到套接字(TCP)的远程地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回给定套接字选项的值
s.setsockopt() 设置给定套接字选项的值
s.shutdown() 关闭连接
s.close() 关闭套接字
s.detach() 在未关闭文件描述符的情况下关闭套接字,返回文件描述符
s.ioctl() 控制套接字的模式(仅支持windows)
面向阻塞的套接字方法
s.setblocking() 设置套接字的阻塞或者非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 获取阻塞套接字操作的超时时间
面像文件的套接字方法
s.fileno() 套接字的文件描述符
s.makefile() 创建于套接字关联的文件对象
数据属性
s.family 套接字家族
s.type 套接字类型
s.proto 套接字协议

创建TCP服务器

首先先来说说服务器设计的一般思路(伪代码):

ss = socket()     #1、创建服务器套接字

ss.bind()      #2、套接字于地址绑定

ss.listen()    #3、监听连接

inf_loop:     #4、服务器无限循环:  

  cs = ss.accept()    #1)接收客户端连接

  comm_loop:      #2)通信循环

    cs.recv()/cs.send()    #①对话(接收/发送)

  cs.close()  #3)关闭客户端套接字

ss.close()    #5、关闭服务器套接字(如有必要)

值得关心的是accept()的调用。该步骤默认会以阻塞的形式开启一个单线程服务器,用于等待客户端的连接,如果连接成功,则会返回一个独立的客户端套接字,用于和即将到来的消息进行交换,直到连接终止(终止方式一般是一方关闭连接或者向另外发送一个空字符串)。

下面的tsTserv.py脚本描述的是一个TCP服务器,接收来自客户端的消息,然后将消息加上时间戳前缀并发送给客户端:

#导入了time.time()和socket模块的所有属性
from socket import *
from time import ctime

HOST = ''   #空白的变量,表示可以使用任何可用的地址
PORT = 21567    #端口号
BUFSIZ = 1024   #缓冲区大小,单位是bite
ADDR = (HOST,PORT)

tcpSerSock = socket(AF_INET,SOCK_STREAM)  #创建一个TCP套接字
tcpSerSock.bind(ADDR)   #绑定地址
tcpSerSock.listen(5)    #启动TCP监听,其中5表示在连接被转接或拒绝之前,传入连接请求的最大数

while True:     #服务器无限循环
    print('服务器等待连接……')
    tcpCliSock,addr = tcpSerSock.accept()   #被动等待客户端的连接,当连接请求出现的时候,会返回一个新的套接字和客户端的地址组成的元组,于该客户端的通信是在这个新的套接字上进行数据的接受和发送s
    print('……来自于:',addr)
    
    while True:     #上述新套接字中进行通信循环
        data = tcpCliSock.recv(BUFSIZ).decode('utf-8')  #接受客户端发送过来的消息,从网络传输过来的是bytes类型的,需要解码
        print(data)
        if not data:    #如果客户端发送的内容为空,认为客户端已经关闭,此时应该退出通信循环
            break
        tcpCliSock.send(('[%s] %s' % (ctime(),data)).encode('utf-8'))  #服务器将处理的内容发送给客户端,需要将字符串类型数据转化为bytes数据。
        
    tcpCliSock.close()  #关闭当前客户端连接,下一步是等待另外一个客户端
tcpSerSock.close()

 

创建TCP客户端

下面给出创建客户端的伪代码:

cs  = socket()  #创建客户端套接字

cs.connect()    #尝试连接服务器

comm_loop:    #通信循环

  cs.send()/cs.recv()  #对话(发送/接收)

cs.close()    #关闭客户端套接字

下面的tsTclnt.py脚本是和上面创建的服务器相关的客户端代码-连接服务器,并以逐行数据的形式提示用户,并展示从服务器返回的数据:

#导入socket模块的所有属性
from socket import *

HOST = '192.168.1.125'  #服务器的主机名,这里是在本地一台计算机上测试,所以这里是本地计算地址
PORT = 21567        #服务器的端口号,必须和服务端的端口号设置一样
BUFSIZ = 1024
ADDR = (HOST,PORT)

tcpCliSock = socket(AF_INET,SOCK_STREAM)    #创建客户端套接字
tcpCliSock.connect(ADDR)        #主动调用并尝试连接服务器

while True:         #无限循环
    data = input('>')       #等待客户端的录入
    if not data:            #如果用户没有输入任何的东西,则退出无限循环
        break
    tcpCliSock.send(data.encode('utf-8'))   #将客户端的数据发送到服务器,并将字符串编码为bytes类型
    data = tcpCliSock.recv(BUFSIZ).decode('utf-8')  #接收服务器返回的数据
    if not data:            #如果服务器终止了或者上一步的recv()方法调用失败的话,也会退出无限循环
        break
    print(data) #正常情况下,从服务器返回的数据会被打印出来
    
tcpCliSock.close()  #关闭客户端套接字
    

运行结果

首先,启动服务器

然后,在另外一台计算机(或者本机,记得更改对应的IP地址呦)上执行客户端脚本。然后就可以进行两台计算机的通信表演了。

这里要特别强调的是,我测试的版本是3.6x,通信端点发送接收内容的一定要进行编码和解码!因为,区别于2.x版本,在python3.x版本中,字符串和bytes是两种不同的数据类型了。

创建UDP服务器

和TCP服务器相比,UDP不需要那么多设置(因为他不是面向连接的),下面是伪代码:

ss = socket()  #创建服务器套接字

ss.bind()    #绑定服务器套接字

inf_loop:    #服务器无限循环

  cs = ss.recvfrom()/ss.sendto()  #关闭(接收/发送)

ss.close()    #关闭服务器套接字

上脚本:

#导入需要的模块
from socket import *
from time import ctime

HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST,PORT)

udpSerSock = socket(AF_INET,SOCK_DGRAM)
udpSerSock.bind(ADDR)   #这里明显和TCP不同,没有所谓的‘监听传入的连接’的动作

while  True:
    print('等待接收消息……')
    data,addr = udpSerSock.recvfrom(BUFSIZ)  #接收数据报
    udpSerSock.sendto(('时间:%s 地址:%s 内容:%s' % (ctime(),addr,data)).encode('utf-8'),addr)  #给客户端返回数据
    print('……接收并返回数据于:',addr)   #服务器上打印记录信息
    
udpSerSock.close()  #一般来说用不到,更优雅的方法是将无限循环放入一个try catch模块中,在捕捉异常或者finally中实现关闭套接字

创建UDP客户端

from socket import *

HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST,PORT)

udpCliSOcket = socket(AF_INET,SOCK_DGRAM)

while True:
    data = input('请输入:')
    if not data:
        break
    udpCliSOcket.sendto(data.encode('utf-8'),ADDR)
    data,ADDR = udpCliSOcket.recvfrom(BUFSIZ)
    if not data:
        break
    print(data.decode('utf-8'),ADDR)
    
udpCliSOcket.close()

看到:UDP和TCP客户端循环的方式基本一样,唯一的区别在于,事先不需要建立与UDP服务器的连接,只是简单的发送一条消息并等待服务器的回复。

你可能感兴趣的:(python网络编程socket编程(TCP、UDP客户端服务器))