Python网络编程学习笔记(《Python核心编程》第2章)

看完本篇博客,你将学会如何用Python制作一个简单的聊天器

套接字(socket)

两种类型:基于文件的和面向网络的

套接字家族:AF_UNIXAF_NETLINKAF_TIPCAF_INET

套接字地址:主机-端口对

有连接的套接字:使用传输控制协议(TCP),使用SOCK_STREAM作为套接字类型

无连接的套接字:使用用户数据报协议(UDP),使用SOCK_DGRAM作为套接字类型

Python中的网络编程

socket()函数

创建套接字的语法:

socket(socket_family, socket_type, protocol=0)

socket_familyAF_UNIXAF_INETsocket_typeSOCK_STREAM
SOCK_DGRAMprotocol 通常省略,默认为0。

import socket

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP/IP套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建UDP/IP套接字
套接字对象方法
名称描述
服务器套接字方法
s.bind()将地址(主机名、端口号对)绑定到套接字上
s.listen()设置并启动TCP 监听器
s.accept()被动接受TCP 客户端连接,一直等待直到连接到达(阻塞)
客户端套接字方法
s.connnect()主动发起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.fileo()套接字的文件描述符
s.makefile()创建与套接字关联的文件对象
数据属性
s.family套接字家族
s.type套接字类型
s.proto套接字协议
创建TCP服务器
伪码描述
ss = socket() 				# 创建服务器套接字
ss.bind() 					# 套接字与地址绑定
ss.listen() 				# 监听连接
inf_loop: 					# 服务器无限循环
    cs = ss.accept() 		# 接受客户端连接
    comm_loop: 				# 通信循环
        cs.recv()/cs.send() # 对话(接收/发送)
    cs.close() 				# 关闭客户端套接字
ss .close() 				# 关闭服务器套接字#(可选)
TCP时间戳服务器
# tsTserv.py
from socket import *
from time import ctime

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

tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(ADDR) # 绑定服务器地址
tcpSerSock.listen(5) # 开启TCP监听器,参数表示在连接被转接或拒绝之前,传入连接请求的最大数

while True:
    print('waiting for connection...')
    tcpCliSock, addr = tcpSerSock.accept() # 等待客户端连接
    print('...connected form:{}'.format(addr))

    while True:
        data = tcpCliSock.recv(BUFSIZ) # 接收TCP消息
        if not data:
            break
        tcpCliSock.send(bytes('[%s] %s' % (ctime(), str(data)), 'utf-8'))
 # 添加事件戳

    tcpCliSock.close()
tcpServerSock.close() # 永远不会被执行

支持IPV6:将AF_INET改为AF_INET6

创建TCP客户端

伪码描述
cs = socket() 			# 创建客户端套接字
cs.connect() 			# 尝试连接服务器
comm_loop: 				# 通信循环
	cs.send()/cs.recv() # 对话(发送/接收)
cs .close() 			# 关闭客户端套接字
TCP时间戳客户端
# tsTclnt.py
from socket import *

HOST = 'localhost'  # or '127.0.0.1'
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(bytes(data, 'utf-8')
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print(data.decode('utf-8'))

tcpCliSock.close()

支持IPV6:将本地主机改成::1,同时将AF_INET改为AF_INET6

执行TCP服务器和客户端

先运行tsTserv.py

waiting for connection...

再运行tsTclnt.py

> hi
[Sat Aug 29 10:08:30 2020] b'hi'
> Hello
[Sat Aug 29 10:08:34 2020] b'Hello'
> 

即可获取时间戳,至于字符串前的b应该是使用bytes转换时导致的

创建UDP服务器

伪码描述
ss = socket()	 					# 创建服务器套接字
ss.bind() 							# 绑定服务器套接字
inf_loop: 							# 服务器无限循环
    cs = ss.recvfrom()/ss.sendto() 	# 关闭(接收/发送)
ss.close() 							# 关闭服务器套接字
对比TCP服务器
ss = socket() 				# 创建服务器套接字
ss.bind() 					# 套接字与地址绑定
ss.listen() 				# 监听连接
inf_loop: 					# 服务器无限循环
    cs = ss.accept() 		# 接受客户端连接
    comm_loop: 				# 通信循环
        cs.recv()/cs.send() # 对话(接收/发送)
    cs.close() 				# 关闭客户端套接字
ss .close() 				# 关闭服务器套接字#(可选)
UDP时间戳服务器
# tsUserv.py
from socket import *
from time import ctime

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

udpSerSock = socket(AF_INET, SOCK_DGRAM)
udpSerSock.bind(ADDR)

while True:
    print('waiting for message...')
    data, addr = udpSerSock.recvfrom(BUFSIZ)
    udpSerSock.sendto(bytes('[%s] %s' % (ctime(), data), 'utf-8'), addr)
    print('...received form and return to:{}'.format(addr))
    
udpSerSock.close()
创建UDP客户端
伪码描述
cs = socket() 					# 创建客户端套接字
comm_loop: 						# 通信循环
	cs.sendto()/cs.recvfrom() 	# 对话(发送/接收)
cs .close() 					# 关闭客户端套接字
UDP时间戳客户端
# tsUclnt.py
from socket import *

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

udpCliSock = socket(AF_INET, SOCK_DGRAM)

while True:
    data = input('> ')
    if not data:
        break
    udpCliSock.sendto(bytes(data, 'utf-8'), ADDR)
    data, ADDR = udpCliSock.recvfrom(BUFSIZ)
    if not data:
        break
    print(data.decode('utf-8'))

udpCliSock.close()

执行UDP服务器和客户端

TCP一致

socket模块属性

image-20200829103154206

image-20200829103246408

socketserver模块

image-20200829103937150

创建socketserver TCP服务器
# tsTservSS.py
from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH)
from time import ctime

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


class MyRequestHandler(SRH):  # 继承SRH
    def handle(self) -> None:	# 接收到客户端消息时调用该程序
        print('...connected from:{}'.format(self.client_address))
        self.wfile.write(bytes('[%s] %s' % (ctime(), self.rfile.readline()), 'utf-8'))


tcpServ = TCP(ADDR, MyRequestHandler)  # 创建服务器
print('waiting for connect...')
tcpServ.serve_forever()
创建socketserverTCP客户端
# tsTclntSS.py
from socket import *

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

while True:
    tcpCliSock = socket(AF_INET, SOCK_STREAM) # 每次都新建一个套接字
    tcpCliSock.connect(ADDR)
    data = input('> ')
    if not data:
        break
    tcpCliSock.send(bytes('%s\r\n' % data, 'utf-8'))
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print(data.decode().strip())
    tcpCliSock.close()

相关模块

image-20200829110503771

习题

(并非标准答案)

面向连接的套接字和无连接套接字之间的区别是什么?

  1. 面向连接的套接字通信前必须要先建立连接,而无连接套接字不需要

  2. 两者采用的协议不同,前者采用TCP协议,后者采用UDP协议

  3. 前者提供序列化、可靠的和不重复的数据交付,没有记录边界,更加可靠

    后者无法保证顺序性、可靠性、重复性,但保存了数据边界

  4. 后者的成本更加低廉,开销更小

TCP 和UDP 之中,哪种类型的服务器接受连接,并将它们转换到独立的
套接字进行客户端通信?

TCP

制作一个简易的聊天器

# chatSer.py, tcp服务器
from socket import *

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

tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)

while True:
    print("waiting for connection...")
    tcpCliSock, addr = tcpSerSock.accept()
    print("connected from addr:{}".format(addr))

    while True:
        data = tcpCliSock.recv(BUFSIZ)
        if not data:
            break
        print(data.decode('utf-8'))
        messa = input("> ")
        tcpCliSock.send(bytes(messa, "utf-8"))

    tcpCliSock.close()
# chatCli.py, tcp客户端
from socket import *

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

tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)

while True:
    messa = input("> ")
    if not messa:
        break
    tcpCliSock.send(bytes(messa, "utf-8"))
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print(data.decode("utf-8"))

tcpCliSock.close()

socketserver实现:

# chatSerSS.py
from socketserver import TCPServer as TCP, StreamRequestHandler as SRH

HOST = ""
PORT = 21567
ADDR = (HOST, PORT)


class MyRequestHandler(SRH):
    def handle(self) -> None:
        self.data = self.request.recv(1024)
        if not self.data:
            return
        print(self.data.decode("utf-8"))
        mess = input("> ")
        self.wfile.write(bytes(mess, "utf-8"))


tcpSer = TCP(ADDR, MyRequestHandler)
print("waiting for connect ...")
tcpSer.serve_forever()
# charCliSS.py
from socket import *

HOST = "localhost"
PORT = 21567
ADDR = (HOST, PORT)

while True:
    tcpCli = socket(AF_INET, SOCK_STREAM)
    tcpCli.connect(ADDR)
    mess = input("> ")
    if not mess:
        break
    tcpCli.send(bytes(mess, "utf-8"))
    ret = tcpCli.recv(1024)
    if not ret:
        break
    print(ret.decode('utf-8'))
    tcpCli.close()

如果你在字符串中见到了b''类型的字符串,可以使用decode()方法解码

如果您觉得我的文章对您有帮助的话,可以点个赞,点个关注,也可以扫描下方二维码关注我。我将在这个公众号上更新自己的学习笔记,以及分享一些算法知识

Study and progress together with me!

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值