>>> 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() 以秒为单位,获得/设置默认套接字超时时间

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服务器的连接,只是简单的发送一条消息并等待服务器的回复。

笑声激发自强,发愤、图强、飞身向上