socket模块
套接字模块是一个非常简单的基于对象的接口,提供对低层BSD套接字样式网络的访问。使用该模块可以实现客户机和服务器套接字。
要在 python 中建立具有 TCP 和流套接字的简单服务器需要使用 socket 模块。利用该模块包含的函数和类定义可生成通过网络通信的程
序。一般来说,建立服务器连接需要6个步骤。
1.创建 socket 对象
创建 socket 对象:socket.socket [ ( family [ , type [ , protocol ] ] ])
family:可以是 AF_UNIX ( UNIX 域,用于同一台机器上的进程间通信),也可以是 AF_INET ( 对于 IPv4 协议的 TCP 和 UDP )或 AF_INET6 ( 对于 IPV6 )。
family 参数指定调用者期待返回的套接口地址结构类型。 family 的值包据3种:AF_INET,AF_INET6 和 AF_UNSPEC。
type:套接字类型可以根据面向连接和非连接分为 SOCK_STREAM(流套接字)或 SOCK_DGRAM(数据报文套接字)。
protocol:一般不填,默认为0
2.将 socket 绑定(指派)到指定地址上:socket.bind(address)。
address 必须是一个双元素元组 ( ( host , port ) ),参数为主机名或 IP 地址+端口号。如果端口号正在被使用或保留,主机名或 IP 地址错误,就会引发 sock.error 异常
3.绑定后必须准备好套接字,以便接受连接请求:socket.listen ( backlog )
backlog 用于指定最多连接数,至少为1,接到连接请求后,这些请求必须排队,如果队列已满,就拒绝请求。
4.服务器套接字通过 socket 的 accept 方法等待客户请求一个连接:connection,address = socket.accept()
调用 accept 方法时,socket 会进入等待(或阻塞)状态。客户请求连接时,accept 方法建立连接并返回服务器。 accept 方法返回一个含有两个元素的元组,如(connection,address)。
第一个元素 ( connection )是新的 socket 对象,服务器通过它与客户通信;第二个元素 ( address)就客户的互联网地址。
5.处理阶段,服务器和客户通过 send 和 recv 方法通信(传输数据)
服务器调用 send,并采用字符串形式向客户发送信息。send 方法返回已发送的字符个数。服务器使用 recv 方法从客户接收信息。调用 recv 时,必须指定一个整数控制本次调用所接收的最
大数据量。recv 方法在接收数据时会进入 blocket 状态,最后返回一个字符串,用于表示收到的数据。如果发送的量超过 recv 允许的量,数据就会被截断。多余的数据将缓冲于接收端。以后
调用 recv时,多余的数据会从缓冲区删除。
6.传输结束,服务器调用 socket 的 close 方法以关闭连接
通信结束后只须使用 socket.close 关闭连接即可
UDP服务端单线程
1 import socket 2 3 #1.创建套接字 4 udp_s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 5 6 #2.绑定IP和端口号 7 udp_s.bind(('192.168.1.102',4395)) 8 9 while True: 10 #接收消息 11 #data是从客户端发送过来的消息,addr是元组格式,存储IP和端口号 12 data,addr = udp_s.recvfrom(1025) 13 print(addr) 14 #打印出客户端发送过来的消息,data.decode('utf-8,'):将字符转为中文格式 15 print(data.decode('utf-8,'),addr) 16 #udp_s.sendto:发送消息。 17 udp_s.sendto('服务器发送的消息'.encode('utf-8'),addr) 18 19 #关闭套接字 20 udp_s.close()
UDP客户端单线程
1 import socket 2 3 #创建套接字 4 udp_c = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 5 6 #绑字端口号 7 udp_c.bind(('192.168.1.102',6969)) 8 9 sendData = input('<<:') 10 11 #发送消息,('192.168.1.102',4395)要和服务端的 udp_s.bind(('192.168.1.102',4395)) 个致 12 udp_c.sendto(sendData.encode('utf-8'),('192.168.1.102',4395)) 13 14 #接收消息 15 data,addr = udp_c.recvfrom(1024) 16 print(data.decode('utf-8'),addr) 17 udp_c.close()
UDP服务端多线程
1 import socket,threading 2 3 #1.创建套接字 4 udp_s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 5 6 def rc(): 7 global addr 8 #接收消息 9 #data是从客户端发送过来的消息,addr是元组格式,存储IP和端口号 10 data,addr = udp_s.recvfrom(1024) 11 print(addr) 12 #打印出客户端发送过来的消息,data.decode('utf-8,'):将字符转为中文格式 13 print(data.decode('utf-8,'),addr) 14 15 def st(): 16 sendData = input('<<:') 17 #udp_s.sendto:发送消息。 18 udp_s.sendto(sendData.encode('utf-8'),addr) 19 20 21 #2.绑定IP和端口号 22 udp_s.bind(('192.168.1.102',4363)) 23 24 while True: 25 26 t1 = threading.Thread(target = rc) 27 t2 = threading.Thread(target = st) 28 t1.start() 29 t2.start() 30 t1.join() 31 t2.join() 32 33 34 #关闭套接字 35 udp_s.close()
UDP客户端多线程
1 import socket,threading 2 3 #创建套接字 4 udp_c = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 5 6 #绑字端口号 7 udp_c.bind(('192.168.1.102',6924)) 8 9 def st(): 10 while True: 11 #input 消息输入框 12 sendData = input('<<:') 13 14 #发送消息,('192.168.1.102',4395)要和服务端的 udp_s.bind(('192.168.1.102',4395)) 个致 15 udp_c.sendto(sendData.encode('utf-8'),('192.168.1.102',4363)) 16 17 def rc(): 18 while True: 19 #接收消息 20 data,addr = udp_c.recvfrom(1024) 21 print(data.decode('utf-8'),addr) 22 23 #发送消息线程 24 t1 = threading.Thread(target = st) 25 #接收消息线程 26 t2 = threading.Thread(target = rc) 27 28 t1.start() 29 t2.start() 30 t1.join() 31 t2.join() 32 udp_c.close()
HTTP协议规定post提交的数据必须放在消息主体中,但是协议并没有规定必须使用什么编码方式。服务端通过是根据请求头中的Content-Type
字段来获知请求中的消息主体是用何种方式进行编码,再对消息主体进行解析。具体的编码方式包括: application/x-www-form-urlencoded 最
常见post提交数据的方式,以form表单形式提交数据。 application/json 以json串提交数据。 multipart/form-data 一般使用来上传文件。
http页面取理请求并返回数据
1 import socket 2 #处理请求的方法 3 def head(): 4 b1 = s.recv(1) # G 5 b2 = b'' 6 str1 = b'' 7 while b2 != b'\r' and b1 != b'\n': 8 b2 = b1 # b2 = T 1 \r 9 b1 = s.recv(1) # E T '' \n 10 str1 += b2 # GET \r 11 print(str1) 12 13 14 # 返回数据 15 def sendData(): 16 # 返回数据 17 # ---英文字符-------------------------------------------------------- 18 sendata = 'hello\r\n' 19 s.send('HTTP/1.1 200 OK\r\n'.encode('ASCII')) 20 s.send(str(len(sendata)).encode('ASCII')) 21 s.send('Content-Type: text/html\r\n'.encode('ASCII')) 22 s.send('\r\n'.encode('ASCII')) 23 s.send(sendata.encode('ASCII')) 24 # ---中文字符-------------------------------------------------- 25 # 返回数据,按照http响应报文的结构返回数据 26 # sendata = '页面不存在\r\n' 27 # s.send('HTTP/1.1 200 OK\r\n'.encode('utf-8')) 28 # s.send(str(len(sendata)).encode('utf-8')) 29 # s.send('Content-Type: text/html\r\n'.encode('utf-8')) 30 # s.send('\r\n'.encode('utf-8')) 31 # s.send(sendata.encode('utf-8')) 32 #创建套接字 33 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 34 35 #绑定地址 36 sock.bind(('',8082)) 37 #监听 38 sock.listen() 39 #接收 40 s,addr = sock.accept() 41 42 #调用--处理请求的方法 43 head() 44 # 调用返回数据 45 sendData() 46 #简单方法重组请求头 47 data = s.recv(1024). 48 Data = data.decode('ASCII') 49 list1 = Data.split('\r\n') 50 print(list1) 51 52 s.close() 53 sock.close() 54 55 #启动本页面,然后在浏览器地址栏输入:http://127.0.0.1:8080/
实例:类的方法http页面取理请求并返回数据(共4个文件)
1.主文件名:webServer.py
1 import socket,threading 2 from sockHeadHandler import SockHanDler 3 from pzfile import PzFile 4 class WebServer: 5 #初始化套接字,等待客户连接 6 def __init__(self): 7 self.skt = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 8 self.skt.bind((PzFile.ip,PzFile.port)) 9 self.skt.listen() 10 #定义主程序启动入口 11 def start(self): 12 # s,addr = self.skt.accept() 13 # #处理头部信息,和返回信息 14 # # headhandler = SockHanDler(s) 15 # # headhandler.startHead() 16 # # s.close() 17 while True: 18 #处理头部信息 和返回信息 19 s,addr = self.skt.accept() 20 if s: 21 headhandler = SockHanDler(s) 22 #开启线程处理多个请求(并发) 23 t1 = threading.Thread(target = headhandler.startHead) 24 t1.start() 25 t1.join() 26 s.close() 27 28 if __name__ == '__main__': 29 #实例化 30 ws = WebServer() 31 #启动接收 32 ws.start()
2.类模块文件名:sockHeadHandler.py
1 from pzfile import PzFile 2 import os 3 class SockHanDler: 4 #1.初始化参数 5 def __init__(self,s): 6 self.s = s 7 8 #2.程序的入口 9 def startHead(self): 10 #调用头处理 11 self.headHandler() 12 13 #调用返回,第4步 14 #self.sendHandler() 15 16 #返回指定页面,第5步 17 18 self.sendpage('index.html') 19 20 #3.头部处理方法 21 def headHandler(self): 22 headInfo = self.__getHead() 23 print(headInfo) 24 25 #4.返回信息处理方法 26 def sendHandler(self): 27 sendData = 'Hello' 28 29 #5.返回页面方法 30 def sendpage(self,file): 31 #拼接文件路径 32 file_path = os.path.join(PzFile.base_path,file) 33 #读取文件 34 with open(file_path,mode='r',encoding='utf-8') as f: 35 html = f.read() 36 self.__sendRst(html) 37 38 #================================================ 39 #2-1.获取头部处理逻辑 40 def __getHead(self): 41 # 简单方法重组请求头 42 data = self.s.recv(1024) 43 Data = data.decode('utf-8') 44 list1 = Data.split('\r\n') 45 return list1 46 #2-2.返回信息的方法 47 def __sendRst(self,data): 48 self.s.send((PzFile.http_v + PzFile.http_s + PzFile.http_ok + PzFile.rn).encode('utf-8')) 49 self.s.send((PzFile.content_type + PzFile.rn).encode('utf-8')) 50 self.s.send(PzFile.rn.encode('utf-8')) 51 self.s.send(data.encode('utf-8'))
3.类模块配置文件:pzfile.py
1 class PzFile: 2 ip = '127.0.0.1' 3 port = 8092 4 5 #返回信息 6 http_v = 'HTTP/1.1' 7 http_s = '200' 8 http_ok = 'OK' 9 10 content_type ='Content-Type: text/html' 11 rn = '\r\n' 12 13 #添加文件目录,复制路径-在webApp目录点击右键-Copy path 14 base_path = r'C:\Users\Administrator\PycharmProjects\untitled\webApp'
4.网页文件:webApp/index.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h1>首页</h1> 9 <p>欢迎来到我的网站</p> 10 <img src = "" alt = "这是美女大图"> 11 <a href = "https://www.baidu.com">点我跳百度</a> 12 </body> 13 </html>