美文网首页
python进阶-06-网络编程

python进阶-06-网络编程

作者: 西海岸虎皮猫大人 | 来源:发表于2020-03-31 00:11 被阅读0次

1 概念

网络编程基于TCP和UDP

IP

用来标识网络中的计算机
主流IPV4,32位整数,每8位分组
C类地址:110+网络号21位+主机号8位
主机号0表示不存在 共可表示254台
IPV6 128位整数
127.0.0.1 本机地址

端口

0-65535
0-1023 被保留
网络程序都有自己的端口
端口用于标识进程

网络通信协议

协议即约定
OSI模型七层模型:
物理层-数据链路层-网络层-传输层-会话层-表示层-应用层
互联网通信使用最多的时TCP/IP协议族
TCP/IP协议族四层:
应用层-传输层-互联网络层-网络接口层

TCP/UDP协议

传输层协议
Socket是传输层给应用层的编程接口
TCP类似拨打电话,需要建立虚拟连接保证可靠连接
UDP类似发送短信,不可靠
TCP安全,稳定,效率低

socket

python
tcp SOCK_STREAM 面向连接
udp SOCK_DGRAM 面向无连接
可借助网络调试助手进行调试


image.png

2 UDP发送数据

# coding=utf-8
from socket import socket, AF_INET, SOCK_DGRAM
# 创建UDP套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 键盘接收发送的信息
data = input('请输入要发送的信息:')
# 创建接收信息的地址
addr = ('192.168.0.110', 8080)
# 发送信息
udp_socket.sendto(data.encode('gb2312'), addr)
udp_socket.close()

控制台输入要发送的信息,网络调试助手可接收到结果
多次启动python程序,会随机分配端口


image.png

3 UDP接收数据

# coding=utf-8
from socket import *
# 创建UDP套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 键盘接收发送的信息
data = input('请输入要发送的信息:')
# 创建接收信息的地址
addr = ('192.168.0.110', 8080)
# 绑定一个端口 ''表示本机 8989端口
udp_socket.bind(('', 8989))
# 发送信息
udp_socket.sendto(data.encode('gb2312'), addr)
# 本次接收最大字节数1024
recv_data = udp_socket.recvfrom(1024)
print('接收到%s的消息:%s'%(recv_data[1], recv_data[0].decode('gb2312')))
# 关闭套接字
udp_socket.close()

执行结果:

请输入要发送的信息:你好
接收到('192.168.0.110', 8080)的消息:收到

4 UDP多线程聊天

# coding=utf-8
from socket import *
from threading import Thread

# 创建UDP套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 绑定一个端口 本机8989端口
udp_socket.bind(('', 8989))


# 接收
def recv_fun():
    while True:
        recv_data = udp_socket.recvfrom(1024)
        print('>>%s:%s'%(recv_data[1], recv_data[0].decode('gb2312')))


# 发送
def send_fun():
    while True:
        addr = ("192.168.0.110", 8080)
        data = input('<<:')
        udp_socket.sendto(data.encode('gb2312'), addr)

if __name__ == '__main__':
    # 创建两个线程
    t1 = Thread(target=send_fun)
    t2 = Thread(target=recv_fun)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

5 TFTP下载器

简单文件下载协议,使用端口69
tftp服务器使用ftpd32软件进行模拟


image.png

tftp文件服务器将文件切分,然后随机创建一个新端口进行发送,69端口可继续接收其他用户的读写请求
服务器发送一个完包,客户端会向服务器发送确认信息
数据包长度小于516表示发送完毕,如果最后一个包刚好为516,则再发送一个长度为0的数据包

# coding=utf-8
import struct
from socket import *

filename = 'test.jpg'
server_ip = '127.0.0.1'
# 封装读请求
send_data = struct.pack('!H%dsb5sb'%len(filename), 1, filename.encode(), 0, 'octet'.encode(), 0)
# 创建套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
udp_socket.sendto(send_data, (server_ip, 69))
# 本地创建文件 a追加模式 b二进制
f = open(filename, 'ab')
while True:
    recv_data = udp_socket.recvfrom(1024)
    # 获取操作码及数据块编号
    caozuoma, ack_num = struct.unpack('!HH', recv_data[0][:4])
    # 判断操作码是否是5,如果是5则为错误信息
    if caozuoma == 5:
        print('文件不存在')
        break
    # 将接收到的数据写入到文件中
    f.write(recv_data[0][4:])
    # 如果读取完
    if len(recv_data[0]) < 516:
        break
    # 发送确认包
    ack_data = struct.pack('!HH', 4, ack_num)
    # 获取服务器发送数据的随机端口
    rand_port = recv_data[1][1]
    udp_socket.sendto(ack_data, (server_ip, rand_port))

6 TCP通信

三次握手
1.客户端发送包含同步标志(SYN)的TCP报文
报文指明客户端端口及TCP序号
2.服务端接收到SYN报文,返回SYN+ACK报文,TCP序号加1,ACK即确认
3.客户端返回一个确认报文ACK给服务器端,TCP序号加1,至此一个TCP连接完成,然后开始数据处理

TCP服务器端代码

客户端使用网络助手,协议类型选择TCP Client

# coding=utf-8
from socket import *
# 创建服务端套接字
server_socket = socket(AF_INET, SOCK_STREAM)
# 绑定端口
server_socket.bind(('', 8989))
# 监听
server_socket.listen()
# 接收客户端的连接
client_socket, client_info = server_socket.accept()
# 接收客户端发送的信息
recv_data = client_socket.recv(1024)
print('接收到%s的消息%s'%(client_info, recv_data.decode('gb2312')))
# 关闭连接
client_socket.close()
server_socket.close()

运行代码,网络调试助手连接,发送消息
控制台会打印服务端接收的消息

7 TCP模拟QQ

服务器端
# coding=utf-8
from socket import *
# 创建服务器端套接字
server_socket = socket(AF_INET, SOCK_STREAM)
# 绑定端口
server_socket.bind(('', 9999))
# 监听
server_socket.listen()
# 等待客户端的连接
client_socket, client_info = server_socket.accept()
while True:
    # 接收客户端的消息
    recv_data = client_socket.recv((1024))
    print('客户端说:', recv_data.decode('utf-8'))
    if recv_data.decode('utf-8') == 'bye':
        break
    # 发送消息
    msg = input('>')
    client_socket.send(msg.encode('utf-8'))
server_socket.close()
客户端
# coding=utf-8
from socket import *
# 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM)
# 调用connect方法与服务器建立连接
client_socket.connect(('127.0.0.1', 9999))
while True:
    # 客户端发送消息
    msg = input('>')
    client_socket.send(msg.encode('utf-8'))
    if msg == 'bye':
        break
    # 客户端接收消息
    recv_data = client_socket.recv(1024)
    print('服务端说:', recv_data.decode('utf-8'))
client_socket.close()

启动服务端和客户端,可以互相发送消息

8 TCP多线程聊天

服务器端
# coding=utf-8
from socket import *
from threading import Thread

sockets = []


def main():
    # 创建server端套接字
    server_socket = socket(AF_INET, SOCK_STREAM)
    server_socket.bind(('', 8888))
    # 监听
    server_socket.listen()

    while True:
        # 接收客户端请求
        client_socket, client_info = server_socket.accept()
        sockets.append(client_socket)
        # 开启线程处理当前客户端的请求
        t = Thread(target=readMsg, args=(client_socket,))
        t.start()


def readMsg(client_socket):
    # 读取客户端的消息
    while True:
        recv_data = client_socket.recv(1024)
        # 将消息发送给所有在线的客户端
        for socket in sockets:
            socket.send(recv_data)


if __name__ == '__main__':
    main()
客户端
# coding=utf-8
from socket import *
from threading import Thread


def readMsg(client_socket):
    while True:
        recv_data = client_socket.recv(1024)
        print('收到:', recv_data.decode('utf-8'))


def writeMsg(client_socket):
    while True:
        msg = input('>')
        client_socket.send(msg.encode('utf-8'))


def main():
    # 创建客户端套接字
    client_socket = socket(AF_INET, SOCK_STREAM)
    # 调用connect连接服务器
    client_socket.connect(('127.0.0.1', 8888))
    # 开启线程处理客户端读消息
    t = Thread(target=readMsg, args=(client_socket,))
    t.start()
    # 开启线程处理客户端写消息
    t = Thread(target=writeMsg, args=(client_socket,))
    t.start()


if __name__ == '__main__':
    main()

9 TCP多线程聊天优化

*1.客户端发送指定字符串退出客户端,服务端移除该线程
*2.显示客户端用户名

服务端
# coding=utf-8
from socket import *
from threading import Thread

sockets = []


def main():
    # 创建server端套接字
    server_socket = socket(AF_INET, SOCK_STREAM)
    server_socket.bind(('', 8888))
    # 监听
    server_socket.listen()

    while True:
        # 接收客户端请求
        client_socket, client_info = server_socket.accept()
        sockets.append(client_socket)
        # 开启线程处理当前客户端的请求
        t = Thread(target=readMsg, args=(client_socket,))
        t.start()


def readMsg(client_socket):
    # 读取客户端的消息
    while True:
        recv_data = client_socket.recv(1024)
        # 如果接收到的消息结尾是bye,则移除该客户端 *1
        if recv_data.decode('utf-8').endswith('utf-8'):
            sockets.remove(client_socket)
            client_socket.close()
            break
        # 将消息发送给所有在线的客户端
        for socket in sockets:
            socket.send(recv_data)


if __name__ == '__main__':
    main()
客户端
# coding=utf-8
from socket import *
from threading import Thread

# 下线标识
flag = True

def readMsg(client_socket):
    global flag
    while flag:
        recv_data = client_socket.recv(1024)
        print('收到:', recv_data.decode('utf-8'))


def writeMsg(client_socket, user_name):
    global flag
    while flag:
        msg = input('>')
        # *2
        msg = user_name + '说:' + msg
        client_socket.send(msg.encode('utf-8'))
        # 如果输入bye下线 *1
        if msg.endswith('bye'):
            flag = False


def main():
    # 创建客户端套接字
    client_socket = socket(AF_INET, SOCK_STREAM)
    # 调用connect连接服务器
    client_socket.connect(('127.0.0.1', 8888))
    user_name = input('请输入用户名:')
    # 开启线程处理客户端读消息
    t1 = Thread(target=readMsg, args=(client_socket,))
    t1.start()
    # 开启线程处理客户端写消息
    t2 = Thread(target=writeMsg, args=(client_socket, user_name))
    t2.start()
    t1.join()
    t2.join()
    client_socket.close()


if __name__ == '__main__':
    main()

相关文章

网友评论

      本文标题:python进阶-06-网络编程

      本文链接:https://www.haomeiwen.com/subject/rinbuhtx.html