网络编程

1 基于TCP的网络编程

  主要参考

  其中客户端需要执行的操作

    1 创建一个套接字

    2 链接connect

    3 处理 处理的时候用得到的套接字使用send和recv方法来发送和接受数据

  服务器的执行操作

    1 创建一个套接字

    2 绑定地址bind

    3 监听listen

    4 接受accept, 它会返回两个值, 第一个值是一个新的套接字, 第二个参数是客户端的地址+端口

<socket.socket fd=228, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 3997)>
('127.0.0.1', 3997)

    5 处理 发送和接受使用的是新的套接字对象来 具体使用send和recv方法

2 实现远程执行命令

  1) 客户端实现不需要太多的问题

  只需要实现能够不断的向服务器提供输入的命令, 有可以获得服务器提供的数据

  要保证死循环不被一致执行, 需要编写一个退出接口

  具体代码如下

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'weihuchao'

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8080))
print("链接成功..")

while True:
    message = input(">> ")
    if not message:
        continue
    if message == "q":
        break
    client.send(message.encode("utf8"))
    data = client.recv(1024)
    print(data.decode("gbk"))

print("再见..")
client.close()

  2) 服务器的编写

  服务器同样需要循环接受和发送执行命令之后的结果

  需要有退出接口

  可以通过创建子进程来实现对命令的执行, 通过subprocesss可以很方便的创建子进程并且控制它的输入和输出

  可以通过subprocesss下的Popen方法来创建一个子进程, 具体参数如下

    args 字符串或者是列表

    bufsize 0无缓冲 1行缓冲 其他值是缓冲区的大小

    stdin/stdout/stderr None表示无重定向, 继承父进程 PIPE表示创建管道

    shell 设置为真表示会在命令前面加当前系统的shell执行

    env 设置环境变量

  可以通过重定向, 把程序执行的结果,错误获得并传输给客户端

  只是重定向管道只能使用一次

  具体代码如下

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'weihuchao'


import socket,subprocess

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8080))
server.listen(5)
print("服务器已经成功启动..")

while True:
    conn, addr = server.accept()
    while True:
        data = conn.recv(1024)
        if not data:
            break
        res = subprocess.Popen(data.decode("utf8"),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        conn.send(res.stderr.read())
        conn.send(res.stdout.read())
    conn.close()

print("服务器已关闭..")
server.close()

3 粘包

  由于TCP是流传输的, 所以容易发生粘包现象

  产生粘包的两个原因

    1) 发送端连续发送的时候, 想等待缓冲区满才发送

    2) 接受方接受数据不及时, 造成缓冲区接受了多个数据包

  归根结底, 还是因为TCP没有消息边界

  解决粘包的办法就是想办法找出边界

  可以通过发送一个数据长度的消息给对方使得对方可以正常取得完整的数据

  但是最好优化数据长度这个数据包, 否则会出现先确认数据包才能读以后的数据

  利用struct模块, 可以将长度数据转化为一个固定长度的数据

  具体的客户端实现如下

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'weihuchao'

import socket,struct

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8080))
print("链接成功..")

while True:
    message = input(">> ")
    if not message:
        continue
    if message == "q":
        break
    client.send(message.encode("utf8"))
    header = client.recv(4)
    dataSize = struct.unpack('i', header)[0]
    recvSize = 0
    recvData = []
    while recvSize < dataSize:
        data = client.recv(1024)
        recvSize += len(data)
        recvData.append(data)
    res = b"".join(recvData)
    print(res.decode("gbk"))

print("再见..")
client.close()

  具体服务器端的代码如下

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'weihuchao'


import socket,subprocess,struct

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8080))
server.listen(5)
print("服务器已经成功启动..")

while True:
    conn, addr = server.accept()
    while True:
        data = conn.recv(1024)
        if not data:
            break
        print(data)
        res = subprocess.Popen(data.decode("utf8"),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        outRes = res.stdout.read()
        errRes = res.stderr.read()
        dataSize = len(outRes) + len(errRes)
        conn.send(struct.pack("i", dataSize))
        conn.send(outRes)
        conn.send(errRes)

    conn.close()

print("服务器已关闭..")
server.close()

 

人若有恒 无所不成
原文地址:https://www.cnblogs.com/weihuchao/p/6807982.html