前言.

        这个程序救了我一命,具体的后面再说

正文.

        作用.

                没啥好说的,作用肯定就是数据传输了,而且这个文件配合一些操作,甚至可以实现传文件

        导入.

                这次要用到一个很强大的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()