网络编程
网路编程基础知识
-
C/S和B/S架构
C : client S : server 客户端,服务端
B : browser S : server 浏览器,服务端
B/S架构本质也是C/S
-
网络的七层协议:(应表会传网数物)
-
物理层:电信号(0和1)
-
数据链路层:把物理层的电信号分组,每一组叫一个数据报或数据帧,每一帧有:报头head和数据data两部分。
头固定18字节:6:发送者地址/6:接收者地址/6:数据类型
-
网络层:
-
ip:ipv4:32位2进制表示:点分十进制表示
-
子网掩码:通过子网掩码和ip判断两个ip是否处于同一个网段,通过ip地址和子网掩码做按位与运算
ip地址: 172.16.10.1: 10101100.00010000.00001010.000000001
子网掩码:255.255.255.0: 11111111.11111111.11111111.000000000
按位与运算:172.16.10.0 10101100.00010000.00001010.000000000 -
ip和mac有转换关系:主要是因为ARP协议和mac地址学习。
-
-
传输层:
-
tcp协议:
- 三次握手、四次挥手
- dos和ddos攻击:拒接服务攻击,分布式的拒接服务攻击
- 端口号:0—65535,0—1023为系统占用端口
-
udp协议:
直接发送,不需要响应,所以数据不可靠。(看视频花屏)
-
端口:
- 通过ip + 子网掩码确定一台设备
- 通过ip + 子网掩码 + 端口号确定一个软件
-
-
会话层:使应用建立和维持会话,并能使会话获得同步。
-
表示层:作用之一是为异种机通信提供一种公共语言,以便能进行互操作。
-
应用层:应用层向应用程序提供服务,这些服务按其向应用程序提供的特性分成组,并称为服务元素。
-
网络编程常用模块
模块 | 描述 |
---|---|
socket | 基于传输层TCP、UDP协议进行网络编程的模块 |
asyncore | socket模块的异步版,支持基于传输层协议的异步通信 |
asynchat | asyncore的增强版 |
cgi | 基本的CGI(Common Gateway Interface,早期开发动态网站的技术)支持 |
E-mail 和 MIME消息处理模块 | |
ftplib | 支持FTP协议的客户端模块 |
httplib、http.client | 支持HTTP协议以及HTTP客户端模块 |
imaplib | 支持IMAP4协议的客户端模块 |
mailbox | 操作不同格式邮箱的模块 |
mailcap | 支持Mailcap文件处理的模块 |
nntplib | 支持NTTP协议的客户端模块 |
smtplib | 支持SMTP协议(发送邮件)的客户端模块 |
poplib | 支持POP3协议的客户端模块 |
telnetlib | 支持TELNET协议的客户端模块 |
urllib | 支持URL处理的模块 |
xmlrpc | 支持XML-RPC协议的服务器和客户端模块 |
接下来我们会主要讨论socket模块
socket模块
程序再使用socket之前,必须先创建socket对象,可以通过以下语法创建socket实例:
s = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
- family 参数用于指定网络类型。总共有三个值:AF_INET(基于IPv4协议的网络)、AF_INET6(基于IPv6协议的网络)、AF_UNIX(UNIX网络)
- type 参数用于指定网络Sock类型。默认是SOCK_STREAM(基于TCP协议的socket)、SOCK_DGRAM(基于UDP协议的socket)、SOCK_RAM(原始socket)
- 后两个可以忽略
那么创好socket对象后就需要区分服务端和客户端了。
socket对象方法
作为服务器端使用的socket必须绑定到指定IP地址和端口,并在该IP地址和端口进行监听,接收来自客户端的连接。
socket对象提供了如下常用方法:
- bind(address):作为服务端使用的socket调用该方法,将socket绑定到指定address元组(IP, 端口)。
- listen([backlog]):服务端的监听方法。
- accept() :服务端接收来自客户端的连接。
- recv(bufsize[,flags]):接收socket中的数据(bytes)。
- recvfrom(bufsize[,flags]):与上方法类似,返回(bytes, address)元组。
- send(bytes[, flags]):向socket发送数据(必须已经建立连接),通常用于基于TCP协议的网络。
- sendto(bytes, address):用于UDP协议网络中发送数据
- sendfile(file, offset=0, count=None):将整个文件内容都发过去。
- connect(address):客服端连接服务器。
- connect_ex(address):与上方法大致相同,只是程序出错时不会抛出异常,而是返回一个错误标识。
- close():关闭连接,回收资源。
使用socket通信(TCP)
-
服务端:
import socket host = socket.gethostname() # 获取本机host server = socket.socket() server.bind((host,8080)) server.listen(5) while 1: c, addr = server.accept() # 与客户端建立连接 c.send(b"start:") while 1: '''通信循环,try为了等待客户端断开后连接其他的客户端''' try: print(f" 33[1;36m{c.recv(1024).decode()} 33[0m") msg = input(">>").encode('utf-8') c.send(msg) except Exception: print("客户端断开连接了!") break
-
客户端:
import socket host = socket.gethostname() # 获取本机host cilent = socket.socket() cilent.connect((host, 8080)) while 1: '''通讯循环''' print(f" 33[1;36m{cilent.recv(1024).decode()} 33[0m") msg = input(">>").encode('utf-8') cilent.send(msg)
粘包问题
TCP协议中的数据都是以数据流(type=SOCK_STREAM)的形式传递的,很容易发生几次send的数据被一次recv(接收),或者被截成了好几段。为了解决粘包问题,我们借用了数据报的思想,给每一条数据加一个固定长度的头。这里引入struct
模块来创建固定的头。
示例:写一个仿SHH的客户端和服务端
-
服务端
import socket import subprocess import struct server = socket.socket() host = socket.gethostname() port = 8081 server.bind((host, port)) server.listen(5) while 1: c, addr = server.accept() print(addr, "连接了 >>>") msg = b"hello " le = struct.pack('i',len(msg)) # 将要发送的数据的长度封装成固定长度为4的二进制数据 c.send(le) # 先发送一个头 c.send(msg) # 再发送数据 try: while 1: ssh = c.recv(1024) obj = subprocess.Popen(ssh.decode('gbk'), shell=True, stdout=subprocess.PIPE , stderr=subprocess.PIPE) # subprocess是与系统交互的模块 msg = obj.stdout.read() or obj.stderr.read() # 必然拿到其中一项 le = struct.pack('i',len(msg)) # 将结果打上头 c.send(le) # 与上一样 c.send(msg) except Exception: print(" 断开连接 !")
-
客户端
import socket import struct client = socket.socket() host = socket.gethostname() port = 8081 client.connect((host,port)) while 1: le = struct.unpack('i', client.recv(4))[0] # 解析头,获取数据长度 print(client.recv(le).decode('gbk')) # shell只接收gbk编码 ssh = input(" 请输入命令 >>") client.send(ssh.encode('gbk')) # 返回执行结果
socketserver模块
socketserver模块实现了多线程通信。使用socketserver创建服务端:
#使用socketserver写服务端
import socketserver
#自己定义一个类,必须继承StreamRequestHandler或BaseRequestHandler
class MyTcp(socketserver.StreamRequestHandler):
#必须重写handle方法
def handle(self):
'''这是服务端与客户端交互的方法'''
try:
while True : #通信循环
# print(self)
#给客户端回消息
#conn对象就是request
#接收数据
print(self.client_address)
data=self.request.recv(1024)
print(data)
if len(data)==0:
return
#发送数据
self.request.send(data.upper())
except Exception:
pass
if __name__ == '__main__':
#实例化得到一个tcp连接的对象,Threading意思是说,只要来了请求,它自动的开线程来处理连接跟交互数据
#第一个参数是绑定的地址,第二个参数传一个类
server=socketserver.ThreadingTCPServer(('127.0.0.1',8009),MyTcp)
#一直在监听
#这么理解:只要来一个请求,就起一个线程交互
server.serve_forever()