一:客户端/服务器架构
1 软件c/s架构
2 网络通信原理:
3 互联网协议:
3socket层
4socket是什么:
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
5 套接字工作流程
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
socket()模块函数用法
1 import socket 2 socket.socket(socket_family,socket_type,protocal=0) 3 socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。 4 5 获取tcp/ip套接字 6 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 8 获取udp/ip套接字 9 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)10 11 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。12 例如tcpSock = socket(AF_INET, SOCK_STREAM)
服务端套接字函数 s.bind() 绑定(主机,端口号)到套接字 s.listen() 开始TCP监听 s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来 客户端套接字函数 s.connect() 主动初始化TCP服务器连接 s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 公共用途的套接字函数 s.recv() 接收TCP数据 s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完) s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完) s.recvfrom() 接收UDP数据 s.sendto() 发送UDP数据 s.getpeername() 连接到当前套接字的远端的地址 s.getsockname() 当前套接字的地址 s.getsockopt() 返回指定套接字的参数 s.setsockopt() 设置指定套接字的参数 s.close() 关闭套接字 面向锁的套接字方法 s.setblocking() 设置套接字的阻塞与非阻塞模式 s.settimeout() 设置阻塞套接字操作的超时时间 s.gettimeout() 得到阻塞套接字操作的超时时间 面向文件的套接字的函数 s.fileno() 套接字的文件描述符 s.makefile() 创建一个与该套接字相关的文件
二:基于tcp协议通信的套接字(简单版本)
客户端:
import socketphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.connect(('127.0.0.1',8001))phone.send(b'hello')data=phone.recv(1024)print('就收服务端的消息:',data)
服务端:
import socketphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind(('127.0.0.1',8001))phone.listen(5)print('start------')conn,client_addr=phone.accept()data=conn.recv(1024)print('收到客户的数据',data)conn.send(data.upper())conn.close()phone.close()
三:基于tcp协议通信的套接字(通信版本)
客户端:
from socket import *client=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1',8001))while True: msg=input(">>>").strip() if len(msg)==0:continue client.send(msg.encode('utf-8')) data=client.recv(1024) print(data)client.close()
服务端
# 服务端必须满足至少三点:# 1 绑定一个固定的ip和port# 2 一直对外提供服务,稳定运行# 3 能够支持并发from socket import *server=socket(AF_INET,SOCK_STREAM)server.bind(('127.0.0.1',8001))server.listen(5)while True: try: conn,client_addr=server.accept() print(client_addr,'连接客户端成功') while True: data=conn.recv(1024) if len(data)==0:break conn.send(data.upper()) conn.close() except ConnectionResetError: break
四:基于ssh实现远程执行命令
from socket import *client=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1',8001))while True: cmd=input(">>>").strip() if len(cmd)==0:continue client.send(cmd.encode('utf-8')) cmd_res=client.recv(1024) print(cmd_res)client.close()
from socket import *import subprocessserver=socket(AF_INET,SOCK_STREAM)server.bind(('127.0.0.1',8001))server.listen(5)while True: conn,client_addr=server.accept() print(client_addr) while True: try: cmd=conn.recv(1024) if len(cmd)==0:break obj=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout=obj.stdout.read() stderr=obj.stderr.read() print(len(stdout)+len(stderr)) conn.send(stdout+stderr) except ConnectionResetError: break conn.close()server.close()
五:粘包问题:
from socket import *client=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1',8001))#tcp协议会将数据量小且发送时间间隔较短的数据合并成一个数据报发送client.send(b'hello')client.send(b'andy')client.send(b'hi')
# 粘包问题是tcp协议流式传输数据的方式导致的# 如何解决粘包问题:接收端能够精确地收干净每个数据包没有任何残留from socket import *import subprocessserver=socket(AF_INET,SOCK_STREAM)server.bind('127.0.0.1',8001)server.listen(5)conn,_=server.accept()datal=conn.recv(5)print('第一次收:',datal)data2=conn.recv(5)print('第二次收:',data2)data3=conn.recv(5)print('第三次收:',data3)
六:模拟ssh实现远程执行命令(解决编报问题终极版)
from socket import *import json,structclient=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1',8001))while True: cmd=input('>>>').strip() if len(cmd)==0:break client.send(cmd.encode('utf--8')) # 先接受报头的大小,解出报头的长度 header_zise=struct.unpack('i',client.recv(4))[0] # 再接收报头 header_bytes=client.recv(header_zise) header_json=header_bytes.decode('utf-8') header_dic=json.loads(header_json) total_size=header_dic['total_size'] print(total_size) cmd_res=b'' recv_rise=0 while recv_rise
from socket import *import subprocess,struct,jsonserver=socket(AF_INET,SOCK_STREAM)server.bind(('127.0.0.1',8001))server.listen(5)while True: conn,client_addr=server.accept() print(client_addr) while True: try: cmd=conn.recv(1024) if len(cmd)==0:break obj=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout=obj.stdout.read() stderr=obj.stderr.read() # 制作报头 header_dic={ 'filename':'a.txt', 'age':18, 'total_size':len(stdout)+len(stderr) } header_json=json.dumps(header_dic) header_bytes=header_json.encode('utf-8') # 发送报头的大小 conn.send(struct.pack('i',len(header_bytes))) # 再发送报头 conn.send(header_bytes) # 再发送文件主体 conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close()server.close()
七:模拟ssh实现远程执行命令(解决粘包问题)
from socket import *import struct,jsonclient=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1'))while True: cmd=input(">>>").strip() if len(cmd)==0:continue client.send(cmd.encode('utf-8')) header=client.recv(4) total_size=struct.unpack('i',header)[0] # 2 接收真正的数据 cmd_res=b'' recv_size=0 while recv_size
# 服务端必须满足至少三点# 1 绑定一个固定的ip 和 port# 2 一直对外提供服务,稳定运行# 3 能够支持并发from socket import *import subprocessimport structserver=socket(AF_INET,SOCK_STREAM)server.bind(('127.0.0.1'))server.listen(5)# 连接循环while True: conn,client_addr=server.accept() print(client_addr) # 通信循环 while True: try: cmd=conn.recv(1024) if len(cmd)==0:break obj=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout=obj.stdout.read() stderr=obj.stderr.read() # 先制作固定长度的报头 header=struct.pack('i',len(stderr)+len(stdout)) #再发送报头 conn.send(header) # 3最后发送真是的数据 conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close()server.close()
八:基于udp协议通信的套接字
import socketclient=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)while True: msg=input('>>>').strip() if len(msg)==0:continue client.sendto(msg.encode('utf-8'),('127.0.0.1',8001)) data,server_addr=client.recvfrom(1024) print(data)server.close()
# 基于tcp通信会将发送的多条数据打包成一个发送过去# 基于udp通信会一条一条的发送数据from socket import *server=socket(AF_INET,SOCK_DGRAM)server.bind(('127.0.0.1',8001))while True: data,client_addr=server.recvfrom(1024) print(data) server.sendto(data.upper(),client_addr)server.close()
九:数据报协议的特点
from socket import *client=socket(AF_INET,SOCK_DGRAM)client.sendto(b'hello',('127.0.0.1',8001))client.sendto(b'llll',('127.0.0.1',8001))client.sendto(b'hhhhh',('127.0.0.1',8001))
from socket import *server=socket(AF_INET,SOCK_DGRAM)server.bind(('127.0.0.1',8001))print(server.recvfrom(1024))print(server.recvfrom(1024))print(server.recvfrom(1024))
十:socketserver模块的使用
1 基于tcp协议通信
# 基于tcp协议通信socketserverfrom socket import *client=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1',8001))while True: cmd=input(">>>").strip() if len(cmd)==0:continue client.send(cmd.encode('utf-8')) data=client.recv(1024) print(data.decode('utf-8'))client.slose()
import socketserverclass Myhanlder(socketserver.BaseRequestHandler): def handle(self): while True: try: data=self.request.recv(1024) if len(data)==0:break self.request.send(data.upper()) except ConnectionResetError: break self.request.close()if __name__ == '__main__': server=socketserver.ThreadingTCPServer(('127.0.0.1',8001),Myhanlder) server.serve_forever()
2 基于udp协议通信
客户端
from socket import *client=socket(AF_INET,SOCK_DGRAM)client.connect(('127.0.0.1',8001))while True: client.send('hello'.encode('utf-8')) data=client.recv(1024) print(data.decode('utf-8'))
服务端
# 基于udp协议进行通信socketserver"""import socketserverclass MyUDPhandler(socketserver.BaseRequestHandler): def handle(self): data,sock=self.request # data是接受的发送过来的数据 self.request相当于server sock.sendto(data.upper(),self.client_address) # self.client_address 相当于对方的ip和端口if __name__ == '__main__': server=socketserver.ThreadingUDPServer(('127.0.0.1',8001),MyUDPhandler) server.serve_forever()"""import socketserverclass MyUDPhandler(socketserver.BaseRequestHandler): def handle(self): data,sock=self.request sock.sendto(data.upper(),self.client_address)if __name__ == '__main__': server=socketserver.ThreadingUDPServer(('127.0.0.1',8001),MyUDPhandler) server.serve_forever()
ps:egon博客socket编程:http://www.cnblogs.com/linhaifeng/articles/6129246.html