七.网络编程
网络编程
Socket介绍
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Python中提供socket.py标准库,非常底层的接口库。
Socket是一种通用的网络编程接口,和网络层次没有一一对应关系。
协议族:
AF表示Address Family,用于socket()第一个参数。
- AF_INET IPV4
- AF_INET IPV6
Socket类型
- SOCK_STREAM 面向连接的流套接字。TCP协议
- SOCK_DGRAM 无连接的数据报文套接字,UDP协议
TCP编程
Socket编程,需要两端,一般来说一个服务端,一个客户端。
TCP服务端步骤:
1)创建Socket对象
2)绑定IP地址Address和端口Port。 bind()方法 端口对应的就是进程
3)开始监听,将在指定的IP端口上监听。listen()方法。
4)获取用于传送数据的Socket对象
socket.accept() -> (socket object, address info)
accept方法阻塞等待客户端建立连接,返回一个Socket对象和客户端地址的二元组。
地址是远程客户端的地址,IPV4它是一个二元组(clientaddr, port)
- 接收数据:recv(bufsize[, flags]) 使用缓冲区接收数据
- 发送数据:send(bytes) 发送数据
示例
import time
#TCP Server
sock = socket.socket() #默认IPv4,TCP
ip = '127.0.0.1'
port = 9999 # 整数
addr = (ip, port)
sock.bind(addr) # 绑定IP和端口
sock.listen() #监听
conn, addrinfo = sock.accept() #返回一个socket对象和一个远端地址,默认阻塞
data = conn.recv(1024) # 接收数据
print(data)
msg = '已接收到数据 {}'.format(data.decode())
conn.send(msg.encode()) #传送字节流数据
conn.close()
sock.close()
socket常用方法
方法 | 描述 |
---|---|
socket.recv(bufsize[, flags]) | 获取数据,默认是阻塞方式 |
socket.recvfrom(bufsize[, flags]) | 获取数据,返回一个二元组(bytes, address) |
socket.recv_into(buffer[, nbytes[, flags]]) | 获取到nbytes数据后,存储到buffer中。如果nbytes没有指定或0,将buffer大小的数据存入buffer中。返回接收的字节数 |
socket.recvfrom_into(buffer[, nbytes[, flags]) | 获取数据,返回一个二元组(bytes, address)到buffer中。 |
socket.send(bytes[, flags) | TCP发送数据 |
socket.sendall(bytes[, flags) | TCP发送全部数据,成功返回None |
s.sendto(string[,flags],address) | UDP发送数据 |
socket.sendfile(file, offset=0, count=None) | 发送一个文件直到EOF,使用高性能的os.sendfile机制,返回发送的字节数。 |
socket.makefile(mode='r', buffering=None,* ,encoding=None, errors=None, newline=None) | 创建一个与该套接字相关联的文件对象。 |
- makefile示例
import threading
import socket
import logging
import time
logging.basicConfig(level=logging.INFO, format='%(thread)d %(threadName)s %(message)s')
#TCP Server
sock = socket.socket() #默认IPV4 TCP
ip = '127.0.0.1'
port = 9999 # 整数
addr = (ip, port)
sock.bind(addr) # 绑定IP和端口
sock.listen() #监听
conn, addrinfo = sock.accept() #返回一个socket对象和一个远端地址,默认阻塞
f = conn.makefile(mode='rw')
line = f.read(10)
print(line)
f.write('Return your msg:{}'.format(line))
f.flush()
f.close()
UDP服务端:
大致与TCP相似, 具体如下
1)创建socket对象
2)绑定IP和port
3)传输数据
接收数据:socket.recvfrom(bufsize[,flags])
发送数据: socket.sendto(string, address)
4)释放资源
UDP客户端:
1)创建socket对象
2)发送数据:socket.sendto(string, address) 发给某地址某信息
3)释放资源
Socketserver
socket编程过于底层,所以很多语言都对socket底层API进行了封装,Python的封装就是socketserver模块
SocketServer简化了网络服务器的编写。
它有4个同步类TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer
2个Mixin类ForkingMixin和ThreadingMixin来支持异步。
class ForingUDPServer(ForkingMixin, UDPServer): Pass
class ForingTCPServer(ForkingMixin, TCPServer): Pass
class ThreadingUDPServer(ThreadingMixin, UDPServer): pass
class ThreadingTCPServer(ThreadingMixin, TCPServer): pass
编程接口:
-
socketserver.BaseServer(server_address, RequestHandlerClass)
需要提供服务器绑定的地址信息,和用于处理请求的RequestHandlerClass类。 -
RequestHandlerClass类必须是BaseRequestHandlerClass的子类
-
BaseRequestHandlerClass
和用户连接(socket连接)的用户请求处理类,Server实例接收用户请求后,
最后会实例化这个类。
它被初始化后,送入3个构造参数:request、client_address、server
以后可以在BaseRequestHandlerClass类的实例上通过。
self.request是和客户端连接的socket对象
self.server是TCPServer的本身
self.client_address是客户端地址。
它会一次调用3个函数,子类可以覆盖。
BaseRequestHandlerClass: #要子类覆盖的方法
def setup(self): #每一个连接初始化
pass
def handle(self): #每一次请求处理
pass
def finish(self): #每一个连接清理
pass
示例
import socketserver
import threading
# 处理用户请求
class MyHandler(socketserver.BaseRequestHandler):
def setup(self):
super().setup()
# TODO
self.event = threading.Event()
def handle(self):
super().handle()
print(self.server, self.client_address, self.request)
# 循环,没有循环,连接处理直接断掉了
while not self.event.is_set():
data = self.request.recv(1024).decode()
msg = "Your msg={}".format(data)
print(msg)
self.request.send(msg.encode())
if not data:
break
def finish(self):
super().finish()
#启动服务 服务器绑定地址
ADDR = ('127.0.0.1', 9999)
server = socketserver.ThreadingTCPServer(ADDR, MyHandler)
server.serve_forever()
server.shutdown()
server.server_close()
总结:
创建服务器的步骤
1.通过对BaseRequestHandler类进行子类化,并覆盖handle()方法来创建请求处理类,此方法将处理传入请求
2.必须实例化一个服务器类,将它传递服务器的地址和请求处理程序类。
3.调用服务器对象的handle_request()或者server_forever()方法
4.调用socket_close()关闭套接字。shutdown()方法等待停止forever()方法