linux及python的补充知识
一、linux知识
1、ifconfig:查看主机的ip地址
inet就是ipv4地址,inet6就是ipv6地址
2、ping:测试远程主机连通性
ping用来检测网络是否正常,或者某台主机是否可以连接
3、虚拟机网卡设置
NAT模式 | 虚拟机跟物理主机共用网络 |
---|---|
bridged模式 | 虚拟机如同一台真实存在的计算机,会占用真实网段的一个ip,虚拟机能够获取局域网的ip地址 |
4、端口
linux系统中的端口有65536个!范围为0-65535
端口是怎样分配的?
0-1023是我们不能操控的,1024-635535是我们可以操控的
分配规则分为知名端口和动态端口
知名端口:
知名端口就是众所周知的端口:范围从0-1023
常见协议及端口 | 协议的基本作用 |
---|---|
FTP(21) | 文件上传和下载 |
SSH(22) | 安全的远程登录 |
Telnet(23) | 远程登录 |
dns(53) | 域名解析 |
http(80) | 超文本传输 |
pop3(110) | 邮件接收 |
https(443) | 加密传输的https |
动态端口
动态端口的范围为1024-65535
之所以称为动态端口,是因为它一般不固定分配某种服务,而是动态分配,用户也可以修改
netstat:可以用来查看有哪些端口
lsof:可以用来查看占用端口的服务是谁,lsof -i:端口号
二、python知识
1、编码和解码
文本总是unicode,由str类型来表示,二进制数据使用bytes来表示;
网络中数据的传输都是以二进制(字节码)的方式来进行的,所以我们需要通过对unicode字符串内容进行编码和解码才能达到数据传输的目的
在python中:
str-->bytes:encode编码,就是将字符串转换为字节码
bytes-->str:decode解码,就是将字节码转换为字符串
常用的编码方案有utf-8(万国码)和gbk(中文),encode和decode默认使用的就是utf-8
encode()和decode()方法可以接收参数
bytes.decode(encoding="utf-8",errors="strict")
str.encode(encoding="utf-8",errors="ignore")
errors指错误的处理方案,默认为strict,编码错误就会报错,ignore就是忽略错误
2、socket简介
它能够实现不同主机之间的进程通信,我们网络上各种各样的服务大多数都是基于socket来完成通信的,例如我们每天浏览网页,qq聊天,收发email等。
socket是网络通信的基本单元,提供的方法可以实现数据的发送和接收
2.1、创建socket
在Python中,使用socket模块的socket类就可以
import socket
socket.socket(AddressFamily,Type)
参数一:AdressFamily:地址簇
socket.AF_INET IPv4(默认)
socket.AF_INET6 IPv6
socket.AF_UNIX 只能够用于单一的unix系统进程间的通信
参数二:Type(类型)
socket.SOCK_STREAM 流式socket,for TCP(默认)
socket.SOCK_DGRAM 数据报式socket,for UDP
socket.SOCK_RAW 原始套接字,可以处理icmp,igmp等网络报文
socket.SOCK_SEQPACKET 可靠的连续数据包服务
2.2、socket应用-udp网络程序发送数据
实现代码如下:
#1、导入模块
import socket
# 2、创建套接字,使用ipv4,udp方式
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 3、数据的传递
"""
sendto参数说明
参数1、数据必须是二进制数据,字符串.encode(),转换为二进制
参数2、元组类型,把参数1的数据发送给谁(ip地址字符串,端口数值)
"""
udp_socket.sendto("helloworld!".encode(),("192.168.150.30",8080))
# 4、关闭套接字
udp_socket.close()
效果如下,发送的地址为windows的地址
2.3、udp网络程序接收数据
import socket
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#recvfrom(1024):从套接字中接收1024字节的数据,此方法会造成程序的阻塞
#如果对方发送了数据,recvfrom会自动接触阻塞,如果没有发送数据,会一直等待
recv_data=udp_socket.recvfrom(1024)
recv_text=recv_data[0].decode()
print("来自:",recv_data[1],"的消息",recv_text)
2.4、udp发送端绑定端口
在发送和接收信息的时候,端口会一直变化,所以我们需要绑定端口
实现代码如下:
import socket
#创建套接字
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#绑定端口:udp_socket.bind(adress),adress是一个元组,元组的第一个元素是字符串类型的ip地址,第二个元素是整数端口号
udp_socket.bind(("192.168.150.25",8888)) #这里的IP地址是自己的ip地址
#发送数据
udp_socket.sendto("hello".encode(),("192.168.150.30",8080))
udp_socket.close()
效果如下:
2.5、udp接收端绑定端口
"""
1、导入模块
2、创建套接字
3、绑定端口
4、接收对方发送的数据
5、解码数据
6、输出显示
7、关闭套接字
"""
import socket
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udp_socket.bind("",6666)#ip可以写为空,默认就是本机ip,如果有多个网卡,会监听所有的网卡
#接收对方发送的数据
recv_data,ip_and_port=udp_socket.recvfrom(1024)
#解码数据并数出显示
print("接收{}的信息{}".format(str(ip_and_port,recv_data.decode())))
udp_socket.close()
3、udp广播
广播地址是专门用于同时向网络中所有工作站进行发送的一个地址,255.255.255.255即是广播地址,所以向255.255.255.255发送消息,就是发送广播消息
实现代码如下:
"""
1、导入模块
2、创建套接字
3、设置广播权限
4、发送数据
5、关闭套接字
"""
# 1、导入模块
import socket
# 2、创建套接字
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 3、设置广播权限
#套接字默认不允许发送广播,需要开启相关权限
#udp_socket.setsockopt(套接字,属性,属性值)
#socket.SOL_SOCKET当前的套接字,socket.SO_BROADCAST广播属性,默认值为False,要设置为True,为可以发送广播
udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,True)
# 4、发送数据
udp_socket.sendto("hello".encode(),("255.255.255.255",8080))
# 5、关闭套接字
udp_socket.close()
注意:广播地址也可以写某一个网段.255,一个局域网内的主机就会收到消息,需要安装网络助手才能收到消息哦
如若我们不设置广播权限运行代码,就会出现如下错误
4、udp聊天器
说明:
1、在电脑中编写一个程序,实现三个功能
2、获取键盘数据,并将其发送给对方
3、接收数据并且显示
4、退出聊天系统
实现代码如下:
import socket
"""
1、发送消息
2、接收消息
3、退出功能
"""
def send_message(udp_socket):
"""发送消息"""
adress = input("请输入接收方地址:")
port = input("请输入接收方端口号:")
message = input("请输入要发送的内容:")
udp_socket.sendto(message.encode(), (adress, int(port)))
def recv_message(udp_socket):
"""接收消息"""
recv_data, ip_and_port = udp_socket.recvfrom(1024)
recv_text = recv_data.decode()
print("接收到的消息为", recv_text)
def run():
"""主要实现函数"""
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
udp_socket.bind(("", 8080))
# 循环打印用户菜单
while True:
print("*" * 20)
print("*** 1、发送消息 ***")
print("*** 2、接收消息 ***")
print("*** 3、退出系统 ***")
print("*" * 20)
menu_num = input("请选择功能:")
if menu_num == "1":
send_message(udp_socket)
elif menu_num == "2":
recv_message(udp_socket)
elif menu_num == "3":
break
else:
print("请输入数字1-3!")
udp_socket.close()
if __name__ == "__main__":
run()
实现效果如下:
发送消息
接收消息
5、tcp网络程序
客户端和服务端索要执行的代码框架如下:
tcp网络程序框架:
5.1 tcp客户端
客户端实现代码如下,要在网络调试助手里选择tcp服务端
"""
1、导入socket模块
2、创建socket套接字
3、建立tcp连接(和服务端建立连接),与udp不同的一点
4、开始发送数据(到服务端)
5、关闭套接字
"""
# 1、导入socket模块
import socket
# 2、创建tcp_socket套接字
tcp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 3、建立tcp连接(和服务端建立连接),与udp不同的一点
#socket.connect(adress)主动初始化tcp服务器连接,adress为元组("ip",port)
tcp_client_socket.connect(("192.168.150.71",8080))
# 4、开始发送数据(到服务端)
tcp_client_socket.send("约吗".encode())
#5、接收数据,recv_data保存的是服务端回复信息的二进制,没有ip及port
recv_data=tcp_client_socket.recv(1024)
#解码
recv_text=recv_data.decode()
print("收到数据:",recv_text)
# 6、关闭套接字
tcp_client_socket.close()
客户端发送数据,服务端接收的效果如下:
服务端回复,客户端收到的效果如下:
服务端回复:
客户端接收:
总结:tcp的网络程序与udp的网络程序的不同之处在哪里?
1、tcp需要建立连接这一步,socket.connect()
2、因为上面已经写好了建立连接的地址,所以发送数据时,不需要再sendto了,直接send
3、因为已经建立了连接,所以在接收数据时,直接写recv即可
4、recv收到的只有服务端回复的消息,不像udp一样,会有ip,端口,和消息
5.2 tcp服务端
"""
1、导入模块
2、创建套接字
3、绑定port和ip
4、开启监听(设置套接字为被动模式)
5、等待客户端连接
6、收发数据
7、关闭连接
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 3、绑定port和ip
tcp_server_socket.bind(("",8080))
# 4、开启监听(设置套接字为被动模式)
#listen的作用:设置套接字为被动模式,不能再主动发送数据
#128:允许接收的最大连接数,此数字在windows上有效,在linux上无效
tcp_server_socket.listen(128)
# 5、等待客户端连接
#accept():开始接受客户端连接,程序默认会进入阻塞状态(等待客户端连接),如果有客户端连接,程序自动向下执行,自动解除阻塞
#recv_data数据含有两部分:
#(1)、返回了一个新的套接字socket,对象类型
#(2)、客户端的ip及port,元组类型
#recv_data=tcp_server_socket.accept()
new_client_socket,client_ip_port=tcp_server_socket.accept()
# 6、收发数据
recv_data=new_client_socket.recv(1024)
recv_text=recv_data.decode()
print("接收到{}的信息:{}".format(str(client_ip_port),recv_text))
#新的套接字也要关闭,不能再和当前的客户端通信
new_client_socket.close()
# 7、关闭连接,表示程序不再接收新的客户端连接,已经连接的就继续服务
tcp_server_socket.close()
在客户端进行发送数据的操作
服务端就能收到了
服务端总结:
5.3 服务端加强版
1、如何实现客户端发送多条消息呢?如果客户端断开网络,服务端该如何处理呢?
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 3、绑定port和ip
tcp_server_socket.bind(("",8080))
# 4、开启监听(设置套接字为被动模式)
tcp_server_socket.listen(128)
# 5、等待客户端连接
new_client_socket,client_ip_port=tcp_server_socket.accept()
# 6、收发数据
while True:
recv_data=new_client_socket.recv(1024)
if len(recv_data)!=0:
recv_text=recv_data.decode()
print("接收到{}的信息:{}".format(str(client_ip_port),recv_text))
else:
print("客户端已经断开连接!")
break
#新的套接字也要关闭,不能再和当前的客户端通信
new_client_socket.close()
# 7、关闭连接,表示程序不再接收新的客户端连接,已经连接的就继续服务
tcp_server_socket.close()
2、上述我们所研究的都是一个客户端,如果有多个客户端发送消息,服务端该如何改动呢
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 3、绑定port和ip
tcp_server_socket.bind(("",8080))
# 4、开启监听(设置套接字为被动模式)
tcp_server_socket.listen(128)
# 5、等待客户端连接
while True:
new_client_socket,client_ip_port=tcp_server_socket.accept()
print("新的就客户端来啦".format(client_ip_port))
# 6、收发数据
while True:
recv_data=new_client_socket.recv(1024)
if len(recv_data)!=0:
recv_text=recv_data.decode()
print("接收到{}的信息:{}".format(str(client_ip_port),recv_text))
else:
print("客户端已经断开连接!")
break
#新的套接字也要关闭,不能再和当前的客户端通信
new_client_socket.close()
# 7、关闭连接,表示程序不再接收新的客户端连接,已经连接的就继续服务
tcp_server_socket.close()
注意:
必须等待第一个客户端断开后,第二个客户端才有机会连接,如果想要实现第一个客户端不用断开,其他客户端就能连接,就得使用多线程,以后再研究。
6、文件下载器
客户端实现代码如下:
"""
将/home/demo/Document/python_projects/day04/1.txt
下载到/home/demo/Desktop/1.txt
1、导入模块
2、创建套接字
3、建立连接,connect()就相当于三次握手
4、接收用户输入的文件名
5、发送文件名到服务端
6、创建文件,并且准备保存
7、接收服务端发来的数据,保存到本地(循环接收)
8、关闭套接字
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 3、和服务端建立连接
tcp_client_socket.connect(("192.168.150.71",8080))
# 4、接收用户输入的文件名
file_name=input("请输入要下载的文件名:\n")
# 5、发送文件名到服务端
tcp_client_socket.send(file_name.encode())
# 6、创建文件,并且准备保存
with open("/home/demo/Desktop/"+file_name,"wb") as file:
# 7、接收服务端发来的数据,保存到本地(循环接收)
while True:
file_data=tcp_client_socket.recv(1024)
#判断数据是否传送完毕
if file_data:
file.write(file_data)
else:
break
# 8、关闭套接字
tcp_client_socket.close()
服务端实现代码如下:
"""
将/home/demo/Document/python_projects/day04/1.txt
下载到/home/demo/Desktop/1.txt
1、导入模块
2、创建套接字
3、绑定端口
4、开启监听,设置套接字为被动
5、接受客户端连接
6、接收客户端发送的文件名
7、根据文件名读取文件内容
8、把读取的内容发送给客户端(循环发送)
9、关闭和当前客户端的连接
10、关闭服务器
"""
#1、导入模块
import socket
# 2、创建套接字
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 3、绑定端口
tcp_server_socket.bind(("",8080))
# 4、开启监听,设置套接字为被动
tcp_server_socket.listen(128)
# 5、接受多个客户端连接
while True:
new_client_socket,client_ip_port=tcp_server_socket.accept()
print("欢迎新客户端:".format(client_ip_port))
# 6、接收客户端发送的文件名
recv_data=tcp_server_socket.recv(1024)
file_name=recv_data.decode()
#异常捕获
try:
# 7、根据文件名读取文件内容
with open(file_name,"rb") as file:
# 8、把读取的内容发送给客户端(循环发送)
while True:
file_data=file.read(1024)
#判断是否读取到了文件的末尾
if file_data:
new_client_socket.send(file_data)
else:
break
except Exception as e:
print("文件{}下载失败".format(file_name))
else:
print("文件{}下载成功".format(file_name))
# 9、关闭和当前客户端的连接
new_client_socket.close()
# 10、关闭服务器
tcp_server_socket.close()
实现效果如下:
7、长连接和短连接
8、基于TCP的web服务器
8.1 模拟浏览器
我们要做的实验是:模拟浏览器客户端去访问某一个网站,我们要访问的网站是www.icoderi.com,访问页面如下:
代码实现如下:
"""
1、导入模块
2、创建套接字
3、建立连接
4、拼接请求协议
5、发送请求协议
6、接收服务器响应内容
7、保存内容
8、关闭连接
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 3、建立连接
tcp_client_socket.connect(("www.icoderi.com",80))
# 4、拼接请求协议
#请求行
request_line="GET / HTTP/1.1\r\n"
#请求头
request_header="Host:www.icoderi.com\r\n"
#请求空行
request_blank="\r\n"
#整体拼接
request_data=request_line+request_header+request_blank
# 5、发送请求协议
tcp_client_socket.send(request_data.encode())
# 6、接收服务器响应内容
recv_data=tcp_client_socket.recv(4096)
recv_text=recv_data.decode()
# 7、保存内容
#只保存空行(\r\n\r\n)后的内容,即为响应主体
#7.1 查询\r\n\r\n的位置
loc=recv_text.find("\r\n\r\n")
#7.2截取字符串
html_data=recv_text[loc+4:]
#7.3 将内容保存在文件里
with open("index.html","w") as file:
file.write(html_data)
# 8、关闭连接
tcp_client_socket.close()
实现效果如下:
8.2 简单的web服务器–返回固定数据
实现的效果就是无论怎样访问,都是返回给客户端hellworld的内容
"""
tcp服务端
1、导入模块
2、创建套接字
3、设置地址重用
4、绑定端口
5、设置监听,让套接字由主动变为被动接收
6、接受客户端连接,定义函数-request_handler()
7、接收客户端浏览器发送的请求协议
8、判断请求协议是否为空,空就是下线了
9、不为空就拼接响应报文
10、发送响应报文
11、关闭套接字
"""
def request_handler(new_client_socket,ip_and_port):
"""接收信息,并且作出响应"""
# 7、接收客户端浏览器发送的请求协议
request_data=new_client_socket.recv(1024)
print(request_data)
# 8、判断请求协议是否为空,空就是下线了
if len(request_data)==0:
print("客户端已经下线".format(ip_and_port))
new_client_socket.close()
# 9、不为空就拼接响应报文
#响应行
response_line="HTTP/1.1 200 OK\r\n"
#响应头
response_header="Server:Python20WS/2.1\r\n"
#响应空行
response_blank="\r\n"
#响应主体
response_body="helloworld"
response_data=response_line+response_header+response_blank+response_body
# 10、发送响应报文
new_client_socket.send(response_data.encode())
#关闭当前连接
new_client_socket.close()
def main():
"""程序主入口"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
# 4、绑定端口
tcp_server_socket.bind(("",8080))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
# 6、接受客户端连接,定义函数 - request_handler()
while True:
new_client_socket,ip_and_port=tcp_server_socket.accept()
request_handler(new_client_socket,ip_and_port)
# 11、关闭套接字
tcp_server_socket.close()
if __name__ =="__main__":
main()
实现效果如下:
查看网页的network,就可以看到我们响应的东西
8.3 返回固定页面
1、要准备好index.html文件
2、代码做如下改动
with open("index.html","rb") as file:
response_body=file.read()
response_data=(response_line+response_header+response_blank).encode()+response_body
# 10、发送响应报文
new_client_socket.send(response_data)
实现效果如下:
8.4 返回指定页面
能够实现根据浏览器不同请求,返回对应网络资源的web服务器
我们在上例的代码中做如下改动
# 8、判断请求协议是否为空,空就是下线了
if len(request_data)==0:
print("客户端已经下线".format(ip_and_port))
new_client_socket.close()
#根据客户端浏览器请求的资源路径,返回请求的资源
#(1)把请求协议解码,得到请求报文的字符串
request_text=request_data.decode()
#(2)得到请求行,再得到请求路径
loc=request_text.find("\r\n")
request_line=request_text[:loc]
print(request_line)
#(3)把请求行进行拆分
request_line_list=request_line.split(" ")
# print(request_line_list)
#(4)得到请求的资源路径
file_path=request_line_list[1]
#(5)设置默认首页
if file_path=="/":
file_path="/index.html"
# 9、不为空就拼接响应报文
#响应行
response_line="HTTP/1.1 200 OK\r\n"
#响应头
response_header="Server:Python20WS/2.1\r\n"
#响应空行
response_blank="\r\n"
#响应主体
try:
#响应主体
with open(file_path,"rb") as file:
response_body = file.read()
except Exception as e:
#修改响应行为404
response_line = "HTTP/1.1 404 Not Found\r\n"
#响应内容为错误信息
response_body="Error!(%s)" % str(e)
#把内容转换为二进制
response_body=response_body.encode()
response_data = (response_line + response_header + response_blank).encode() + response_body
效果如下:
总结:
重点在于我们要拿到请求行的信息,就知道要返回的路径了,先通过find到loc的位置找到请求行,然后再利用spilt对其进行切片,分割单位是空格,找到文件路径,最后在读取文件那里做改动。而且我们还做了异常捕获,访问的页面不存在就提示错误,并且如果访问的时候不加路径就直接访问index.html文件
8.5 面向对象封装
import socket
class WebServer(object):
def __init__(self):
"""初始化方法"""
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", 8080))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
#定义实例属性,保存套接字对象
self.tcp_server_socket=tcp_server_socket
def start(self):
"""启动web服务器"""
# 6、接受客户端连接,定义函数 - request_handler()
while True:
new_client_socket, ip_and_port = self.tcp_server_socket.accept()
self.request_handler(new_client_socket, ip_and_port)
def request_handler(self,new_client_socket,ip_and_port):
"""接收信息,并且作出响应"""
# 7、接收客户端浏览器发送的请求协议
request_data=new_client_socket.recv(1024)
# 8、判断请求协议是否为空,空就是下线了
if len(request_data)==0:
print("客户端已经下线".format(ip_and_port))
new_client_socket.close()
#根据客户端浏览器请求的资源路径,返回请求的资源
#(1)把请求协议解码,得到请求报文的字符串
request_text=request_data.decode()
#(2)得到请求行,再得到请求路径
loc=request_text.find("\r\n")
request_line=request_text[:loc]
print(request_line)
#(3)把请求行进行拆分
request_line_list=request_line.split(" ")
# print(request_line_list)
#(4)得到请求的资源路径
file_path=request_line_list[1]
#(5)设置默认首页
if file_path=="/":
file_path="/index.html"
# 9、不为空就拼接响应报文
#响应行
response_line="HTTP/1.1 200 OK\r\n"
#响应头
response_header="Server:Python20WS/2.1\r\n"
#响应空行
response_blank="\r\n"
try:
#响应主体
with open(file_path,"rb") as file:
response_body = file.read()
except Exception as e:
#修改响应行为404
response_line = "HTTP/1.1 404 Not Found\r\n"
#响应内容为错误信息
response_body="Error!(%s)" % str(e)
#把内容转换为二进制
response_body=response_body.encode()
response_data = (response_line + response_header + response_blank).encode() + response_body
# 10、发送响应报文
new_client_socket.send(response_data)
#关闭当前连接
new_client_socket.close()
if __name__ =="__main__":
ws=WebServer()
ws.start()