前言.
这个程序救了我一命,具体的后面再说
正文.
作用.
没啥好说的,作用肯定就是数据传输了,而且这个文件配合一些操作,甚至可以实现传文件
导入.
这次要用到一个很强大的python第三方库——socket
不用非常仔细地去研究这个库,不过还是要背一些东西的
UDP协议
今天就要用这个协议来传输
作为一种无连接协议,UDP他只管发送,不管接收(或者只管接收,不管发送),说白了就是我发出去就行了,接没接到关我屁事。也就是说,它可以绑定到任意端口(无论开没开),也能发送数据到任意端口。众所周知,一台机器不是所有端口都打开的(也许有,反正我没见过),而利用UDP的无连接特性,可以完美解决这种因为端口没打开而无法传输的问题,这也是为什么今天要用UDP。
TCP协议
这是一种有连接协议
这个需要目标机器开启,且目标端口开启,如果没开启会连接失败,而且如果你连接成功,但是如果程序运行完了,你却没关闭连接,就会导致端口占用,然后就不能再连接了(除非手动关闭)。
思路.
如果要实现你说一句,我回一句,但如果你不说,我也不能说,你说完,我才能说,而且我不说完,你发的所有信息我都接收不到的这种功能,只用写一种程序。但想实现像微信、QQ那样实时传输,就要写两个(也不复杂,一个接收、一个发送,搞懂原理后其实很简单)
需要用到的部分工具.
1.socket
这里详细讲一下socket
socket可以指定协议,修改默认参数,发送、绑定、接收等等
首先是指定协议
你需要把协议指定到一个变量(变量名不能是python关键字,之前我用了python关键字socket,导致无法发送),如把UDP协议指定给变量s:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
AF_INET指我们要用IPv4(类似123.123.123.123这种,IPv6是aa:aa:aa:aa:aa:aa这种),SOCK_DGRAM指UDP协议
这样就可以用s变量来做一些操作了
然后是修改默认参数
修改默认参数在UDP里作用不大,比较常用的只有一条:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
这个可以设置端口复用,避免出现address already in use(端口被占用)
接着是发送、绑定、接收
首先是发送
发送需要指定发送内容(需要是字节,不能是其他类型)、目标IP(不必开启)、目标端口(不必开放),如我想发送"Hello"到192.100.100.100的12345端口:
client = 'Hello'
s.sendto(client.encode(), ('192.100.100.100', 12345))
client.encode()指把字符串类型的'Hello'转为字节类型
其实字节也可以用b'Hello'来表示,b表示bytes(字节)
(b''里面的东西不能是中文,不过client变量里面倒是能填中文,然后用encode来转换)
然后是绑定
用bind函数(感觉像谐音)
bind需要指定IP和端口,如我要绑定到192.160.100.100的123端口:
s.bind(('192.160.110.110', 123))
(注意:要两个括号)
对了,如果是绑定到本机,IP可以留空,但不能少双引号
最后是接收
用recvfrom函数(前提是要先绑定)
recvfrom要指定接受大小,然后会返回一个套接字,最大只能接收(发送)65435个字节,不然会导致缓冲区溢出。
如我要接收一个字节数小于等于200的信息(完整):
s.bind(("", 22))
response = s.recvfrom(200)
但是现在response里是内容加上套接字,如果只想获取内容,得这么写:
response = s.recvfrom(200)[0]
不过response是字节类型,想把它转为字符串,得这样:
response = response.decode()
如果想获取IP和端口:
addr = s.recvfrom(200)[1]
IP = addr[0]
port = addr[1]
如果都想获取:
response, addr = s.recvfrom(200)
response就是内容,addr[0]就是IP,addr[1]就是端口
不过这还不算完,还要说一下TCP
如果要指定TCP给一个变量:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
这里一定要指定端口复用
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
然后是连接、发送、绑定、监听
连接(IP是192.1.1.1,端口是22):
s.connect(('192.1.1.1', 22))
如果连接成功,不会输出;如果失败,会输出Connection refused
发送(前提是连接成功):
client = 'Hello'
s.send(client.encode())
绑定和监听(前提是绑定成功):
s.bind(("192.1.1.1", 22))
s.listen(5)
response, addr = s.accept()
addr是一个套接字,addr[0]是IP,addr[1]是端口
2.open函数
这个函数用来打开文件
open函数接收一个路径和一个操作符,然后要赋给一个变量
操作符有常用的三个参数:'w','a','r'
'w'用来覆盖写入,也就是说不管目标文件里面有什么,全部擦除,然后写入新的内容
'a'用来追加写入,也就是说不管目标文件里面有什么,在最后一个字符后面再写入新的内容
'r'用来读取,也就是说不管目标文件里面有什么,全部记录并赋给变量
然后open赋给变量后,变量有write()(写)、read()(读)两种操作,具体见下:
f = open('D:\python\text.txt', 'w') #覆盖写入
f.write('Hello') #写入'Hello'
这样,text.txt这个文件里就只有Hello五个字符
f = open('D:\python\text.txt', 'a') #追加写入
f.write(',word!') #写入',word!'
这样,text.txt这个文件里就有Hello,word!十一个字符
f = open('D:\python\text.txt', 'r') #读取
response = f.read() #把文件内容赋给response
这样,response的值就是'Hello,word!'
代码.
服务端.
import os
import socket
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", 22))
response, addr = s.recvfrom(4096)
if response != 'Do not show':
print("收到来自" + addr[0] + "的连接请求")
s.close()
time.sleep(1)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.sendto(b'', (addr[0], 22))
print("连接已建立")
flag = 0
s.close()
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", 22))
response, addr = s.recvfrom(4096)
print("收到信息:" + response.decode())
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if "你好" in response.decode():
client = "你好"
s.sendto(client.encode(), (addr[0], 22))
s.close()
print("本机回复:你好")
elif "=?" in response.decode():
client = response.decode()
start = 0
a = ""
while client[start] != "+" and client[start] != "-" and client[start] != "*" and client[start] != "/":
a += client[start]
start += 1
a = int(a)
c = client[start]
b = ""
start += 1
while client[start] != "=":
b += client[start]
start += 1
b = int(b)
if c == "+":
ans = str(a + b)
elif c == "-":
ans = str(a - b)
elif c == "*":
ans = str(a * b)
elif c == "/":
ans = str(a / b)
else:
ans = "???"
s.sendto(ans.encode(), (addr[0], 22))
s.close()
print("本机回复:" + ans)
elif "再见" in response.decode():
client = "再见"
s.sendto(client.encode(), (addr[0], 22))
s.close()
print("本机回复:再见")
print("结束聊天")
break
elif "给爷关闭" == response.decode():
client = '特殊命令!退出程序......'
s.sendto(client.encode(), (addr[0], 22))
s.close()
flag = 1
print("本机回复:特殊命令!退出程序......")
break
elif "命令模式" == response.decode():
client = "命令模式"
s.sendto(client.encode(), (addr[0], 22))
s.close()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
response, addr = s.recvfrom(4096)
if response.decode() == "结束命令模式":
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
shu = "已结束"
s.sendto(shu.encode(), (addr[0], 22))
s.close()
break
shu = "\n" + os.popen(response.decode()).read()
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.sendto(shu.encode(), (addr[0], 22))
s.close()
print("本机回复:" + shu)
elif response.decode() == "clear":
shu = os.popen(response.decode()).read()
s.close()
elif "请说:" in response.decode():
client = response.decode()
client = client[3:len(client)]
s.sendto(client.encode(), (addr[0], 22))
s.close()
elif response.decode() == "文件下载":
s.bind(("", 22))
lujing = s.recvfrom(4096)[0]
s.close()
lujing = lujing.decode()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if lujing[len(lujing) - 1] == '/':
lujing = lujing[0:len(lujing) - 1]
print("尝试打开" + lujing)
f = open(lujing, 'r')
print("打开成功")
response = f.read()
s.sendto(response.encode(), (addr[0], 22))
print("已发送")
s.close()
else:
client = "我的主人尚未设置对此消息的回复"
s.sendto(client.encode(), (addr[0], 22))
s.close()
print("本机回复:我的主人尚未设置对此消息的回复")
if flag == 1:
break
就是文件下载还有算数的地方比较麻烦,你也可以拓展一些其他功能,有些地方可能不理解,我再把客户端代码贴上
客户端.
import sys
import socket
ip = input("连接到:")
print("连接中(若连接时间过长,可自行停止)")
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.sendto(b'', (ip, 22))
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", 22))
try:
response, addr = s.recvfrom(4096)
print("连接成功")
except KeyboardInterrupt:
print("连接失败")
s.close()
sys.exit()
s.close()
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
client = input("发送消息:")
if client == "文件下载":
pan = input("路径:")
s.sendto(client.encode(), (ip, 22))
lu = input("保存路径:")
f = open(lu, 'w')
s.sendto(pan.encode(), (ip, 22))
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", 22))
response = s.recvfrom(65435)[0]
if response.decode() == "error":
print("服务端没有对应文件,取消下载......")
s.close()
continue
f.write(response.decode())
f.close()
s.close()
print("下载成功")
continue
s.sendto(client.encode(), (ip, 22))
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", 22))
response, addr = s.recvfrom(4096)
print("收到回复:" + response.decode())
s.close()
if "再见" in client or "......" in response.decode():
break
写在最后,救命程序
之前不是运行了病毒吗(可以翻我以前的文章)?然后重装了系统,由于之前电脑上有一些比较重要的文件,我怕重装后就没了,然后利用没关闭的终端,运行了这个程序上传文件到另一个电脑,然后重装系统,再敲了一个接收文本的普通程序,然后从另一个电脑再上传回来。
啧,我这机智的大脑(我知道很不要脸,但我还是要说)
拓展-基于UDP的聊天器
直接放代码
发送端.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ip = input("IP:")
try:
while True:
client = input("")
s.sendto(client.encode(), (ip, 22))
except:
s.close()
接收端.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", 22))
ip = ""
try:
while True:
response, addr = s.recvfrom(65435)
if addr[0] != ip:
print(addr[0]:)
ip = addr[0]
print(response.decode())
except:
s.close()