python通过socket实现tcp/udp通信

# 网络通信开发基础-脚本开发-第一关

人生没有白走的路,你走的每一步都算数。

任务目的

建立 socket 连接通道,可以相互之间传输数据

采用语言

python

基础原理

OSI七层模型

image-20220620162744236

各层的基本作用

image-20220620162831657

socket通信主要实现于传输层

传输层功能

image-20220620163013574

tcp和udp

tcp

tcp(传输控制协议),面向连接,也就是说tcp连接的建立和释放,需要经过三次握手和四次挥手。同时在建立连接后的数据传输过程中,tcp会有四个机制来保证数据可靠传输;

1.确认应答和序列号
2.超时重传
3.流量控制
4.拥塞控制

通过这些机制,确保数据不会丢失。这很明显,是tcp面向连接的有点。因为这样,也导致存在了一些缺点。

慢,效率低,占用系统资源高,易被攻击。

udp

udp(用户数据报协议),是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。 在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、 计算机的能力和传输带宽的限制; 在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。

由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等, 因此一台服务机可同时向多个客户机传输相同的消息。

udp的报头短,只有8个字节,相比tcp的最小20字节来说,开销更小

udp吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、 源端和终端主机性能的限制

因为无连接,所有udp不保证可靠交付,只保证把数据发出去就行。没有丢失重传机制。

区别

1、基于连接与无连接;

2、对系统资源的要求(TCP较多,UDP少);

3、UDP程序结构较简单;

4、流模式与数据报模式 ;

5、TCP保证数据正确性,UDP可能丢包;

6、TCP保证数据顺序,UDP不保证。

python实现socket通信

socket介绍

socket又称“套接字”,socket会通过udp/tcp协议来发送数据,用来实现两台机器的简单通信.socket是基于C/S架构的,所以socket网络编程,需要编写客户端程序和服务端程序。

image-20220620164755170

socket通信流程

TCP通信

image-20220620164859388

socket关键函数介绍
函数 描述
socket() 获取socket类对象
bind((hostname, port)) 在指定主机的端口绑定监听
listen() 在绑定端口上开启监听,参数表示最大等待建立连接的个数
accept() 等待客户端连接,连接后返回客户端地址
send(data) 发送数据,data 是二进制数据
recv(buffer) 表示接收数据, buffersize 是每次接收数据的长度
close() 关闭套接字连接
connect((hostname, port)) 设置要连接的主机名称与端口号
代码及介绍

server

import socket
# 创建一个socket对象,默认TCP套接字
s = socket.socket()
# 绑定端口
s.bind(('127.0.0.1',9999))
# 监听端口
s.listen(10)
print("正在连接中……")

# 建立连接之后,持续等待连接
while 1:
    # 阻塞等待连接
    sock,addr = s.accept()
    print(sock,addr)
    # 一直保持发送和接收数据的状态
    while 1:
        text = sock.recv(1024)
        # 客户端发送的数据为空的无效数据
        if len(text.strip()) == 0:
            print("服务端接收到客户端的数据为空")
        elif text.decode() == "exit":
            print("客户端下线")
            break
        else:
            print("收到客户端发送的数据为:{}".format(text.decode()))
            content = input("请输入发送给客户端的信息:")
            # 返回服务端发送的信息
            sock.send(content.encode())
    sock.close()

client

import socket
# 创建一个socket对象
s1 = socket.socket()
s1.connect(('127.0.0.1',9999))
# 不断发送和接收数据
while 1:
    send_data = input("客户端要发送的信息:")
    # socket传递的都是bytes类型的数据,需要转换一下
    if send_data=="exit":
        info="exit"
        s1.send(info.encode())
        break
    else:
        s1.send(send_data.encode())
        # 接收数据,最大字节数1024,对返回的二进制数据进行解码
        text = s1.recv(1024).decode()
        print("服务端发送的数据:{}".format(text))
        print("------------------------------")

注:服务端可以持续监听连接,客户端下线,服务端自动断开连接。客户端再次上线,服务端建立连接

效果图

image-20220620220755261

UDP通信

image-20220621103206421

流程:

  1. 导入包socket
  2. 创建一个套接字
  3. 收发消息(优先发送)
  4. 关闭套接字
client
import socket


def main():

   ip = "127.0.0.1"  # 对方ip和端口
   port = 8888
   other_addr = (ip, port)
   byte = 1024
   udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 创建socket

   while True:
       send_data = input("输入要发送的信息:").encode("utf-8")
       udp_socket.sendto(send_data, other_addr)
       """输入数据为空退出,否则进入接收状态"""
       if send_data:
           recv_data, other_addr = udp_socket.recvfrom(byte)
           print("收到来自%s的消息: %s" % (other_addr, recv_data.decode("utf-8")))
       else:
           break
   udp_socket.close() 


if __name__ == '__main__':
   main()

server

流程:

  1. 导入包socket
  2. 创建一个套接字
  3. 绑定信息
  4. 收发消息(优先接收)
  5. 关闭套接字
import socket


def main():
    ip = ""
    port = 8888
    own_addr = (ip, port)  # 接收方端口信息
    byte = 1024
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(own_addr)  # 绑定端口信息
    while True:
        recv_data, other_addr = udp_socket.recvfrom(byte)
        print("收到来自%s的消息: %s" % (other_addr, recv_data.decode("utf-8")))
        send_data = input("输入要发送的信息:").encode("utf-8")
        udp_socket.sendto(send_data, other_addr)
        """输入数据为空退出"""
        if send_data:
            pass
        else:
            break
    udp_socket.close()  # 关闭socket

if __name__ == '__main__':
    main()
效果图

image-20220620222751159

扩展-远程执行命令

server

from  socket import *
import subprocess
import struct

# address=('127.0.0.1',10000)
# back_log=5
# buffer_size=1024

tcp_server=socket(AF_INET,SOCK_STREAM)  ##定义tcp协议的socket接口
tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) ##加入这条socket配置,重用IP和端口,还可以在操作系统上配置ip和端口重用
tcp_server.bind(('127.0.0.1',10000))   ##为套接字绑定IP和端口,(IP和端口可以标示全网唯一)
tcp_server.listen(5)  ## 可以接收的接收的连接数,并且监听端口

while True:
    conn,addr=tcp_server.accept()  ##建立链接,并堵塞端口
    print(addr)
    while True:
        try:  ## 异常处理,防止客户端不正常断开
            data_cmd = conn.recv(1024)   ## 接收消息
            if not data_cmd:break   ##防止接收的消息为空
            print('收到客户端的命令'+str(data_cmd.decode()))
            ## 通过subprocess.Popen执行操作系统命令,并且把返回值重定向到subprocess.PIPE的管道零时存放
            res = subprocess.Popen(data_cmd.decode('utf-8'), shell=True,stderr=subprocess.PIPE,\
                                   stdout=subprocess.PIPE,stdin=subprocess.PIPE)
            err = res.stderr.read()  ##通过res.stderr.read()读取错误消息
            if err:
                res_data=err
            else:
                res_data=res.stdout.read()  ##通过res.stdout.read()读取标准输出消息
            if not res_data:
                res_data = '执行成功'.encode('gbk')  ##防止操作系统执行命令后,返回值为空时,为客户端返回此消息
            length = len(res_data)  ## 计算返回值数据长度
            data_length = struct.pack('i', length)  ## 通过struct.pack处理返回值数据长度,并且也只能占用4个字节
            conn.send(data_length)   ## 发送返回值数据长度(4字节)作为返回给客户端消息的包头,因为是定长的,所\
            # 以客户端在接收时,第一次接收4个字节,就知道服务端返回的消息长度,就可以到自己的内存按次长度获取数据
            conn.send(res_data)  ## 发送数据
        except Exception as e:
            print(e)  ##处理方式
            break

client

from socket import *
import subprocess
import struct
# ip_port=('127.0.0.1',10000)
buff_size=1024

tcp_client=socket(AF_INET,SOCK_STREAM)
tcp_client.connect(('127.0.0.1',10000))   ## 链接服务端

while True:
    cmd_data=input('请输入命令:')    ## 输入命令
    if not cmd_data:continue       ##判断是否输入空值,为空跳出本次循环,让用户重新输入
    if cmd_data == 'quit': break    ## 当用户输入quit时,断开链接(相当于不正常断开)
    tcp_client.send(cmd_data.encode('utf-8'))  ## 发送命令

    length_data = tcp_client.recv(4)    ## 第一次接收4字节,就是服务端用struct.pack封装为4字节的包头
    head_data=struct.unpack('i',length_data)[0]  ## 街封装,结果是列表类型

    # 处理数据
    res_length=0
    res_msg=b''
    while res_length < head_data:
        res_msg += tcp_client.recv(buff_size)
        res_length += len(res_msg)
        print(res_length)
    print('命令的执行结果是 ', res_msg.decode('gbk'))
tcp_client.close()

image-20220621104812312

posted @ 2022-06-21 15:07  木捏牛  阅读(1412)  评论(0编辑  收藏  举报