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_TIPCpython支持的套接字地址家族
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服务器的连接,只是简单的发送一条消息并等待服务器的回复。

posted on 2018-04-11 16:22 风雨一肩挑 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/hiwuchong/p/8783754.html

评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符 “速评一下”
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页