Python学习笔记(五)Socket编程

Socket编程

一个简单的Socket 编程

Socket通信流程如下图:
Socket通信流程
下面写一个最简单的Socket通信程序,假设服务器与客户端都在本机(127.0.0.1),模拟发送信息的过程,sever.py表示服务端,client.py表示客户端。需要注意,在Python3中,Socket的send方法只能发送bytes数据,所以需要把str数据强制转换为bytes类型,编码类型为UTF-8。

sever.py代码如下:

# sever.py
import socket

sk = socket.socket()

address = ('127.0.0.1', 8848)  # bind方法的参数是一个元组,由IP地址和端口号(自己任意添加)组成
sk.bind(address)  # 为Socket绑定IP和端口号

sk.listen(3)  # 监听设置端口,等待客户端的请求。在服务端与某客户端连接时,最多有3个客户端可以为等待状态
print('Waiting...')

conn, addr = sk.accept()  # Accept阻塞,直到有客户端连接进来
print('Connected!')

inp = input()
conn.send(bytes(inp, encoding='utf8'))  # 把发送的数据转为bytes类型,编码格式为UTF-8
print('服务端向客户端发送信息')

data = conn.recv(1024)  # 1024表示接受的最大数据量
print(str(data, encoding='utf8'))

client.py代码如下:

# client.py
import socket

sk = socket.socket()

address = ('127.0.0.1', 8848)  # 地址与服务端相同
sk.connect(address)

data = sk.recv(1024)
print(str(data, encoding='utf8'))

inp = input()
sk.send(bytes(inp, encoding='utf8'))
print('客户端向服务端发送信息')

首先运行sever,显示:Waiting…
在这里插入图片描述
运行client之后,再看sever,显示:Connected!
在这里插入图片描述
这时在sever编写一条信息发送,severclient的显示分别如下:
在这里插入图片描述
在这里插入图片描述
这时在client编写一条信息,发送后,severclient显示分别如下:
在这里插入图片描述
在这里插入图片描述

在程序中要注意的问题是,客户端向服务端发送信息,用自己的 sk.send() 就可以,而服务端向客户端发送信息要使用由 sk.accept() 得到的 conn的send方法来发送。因为服务器会和多个客户端连接,每个连接都会生成新的Socket,在上面的程序中conn就是代表这个新的Socket,它表示服务器与客户端的连接,如果要断开这个连接,调用 conn.close() 方法。他们的关系可以这样表示:
在这里插入图片描述


不间断聊天

# ---------- sever.py ----------
import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 8848))

sk.listen(3)  # 最多有3个客户端在等待
print('Sever waiting...')

while True:
    conn, addr = sk.accept()  # 连接客户端
    print('Connected!')

    while True:
        client_data = conn.recv(1024)
        client_data = str(client_data, encoding='utf8')
        if client_data == 'exit':
            break
        print(client_data)

        sever_response = input('>>>')
        if sever_response == 'exit':
            break
        conn.send(bytes(sever_response, encoding='utf8'))

    conn.close()

# ====================================================================
#  ---------- client.py ----------
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 8848))

while True:
    inp = input('>>>')
    sk.send(bytes(inp, encoding='utf8'))
    if inp == 'exit':
        break

    sever_response = sk.recv(1024)
    print(str(sever_response, encoding='utf8'))

多次运行client,最多可以同时运行4个(1个与服务端连接,另外3个在等待),再多就会报错。在一个client断开连接时,排队的client依次接上。


发送命令

发送cmd命令实质没有新内容,只需要掌握如何用 subprocess 模块来执行cmd命令就可以了,看一个简单的例子:

import subprocess

cmd = input('>>>').strip()

cmd_call = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
cmd_result = cmd_call.stdout.read()

print(str(cmd_result, encoding='gbk'))

远程发送命令结果,把 cmd_result 发送即可,需要注意命令行编码格式为GBK,所以强制转换时类型应为GBK。


粘包现象

假设在程序中有这么一段代码:

# 发送端
conn.sendall(result_len)  # 期望作为 int 类型
conn.sendall(result)  # 期望作为 str 类型

# 接收端
result_len = int(str(sk.recv(1024), encoding='utf8'))
result = str(sk.recv(1024), encoding='utf8')

而在接收端会把 result_len 当做 int 类型来处理,但是当强制转化为 int 时可能出错,因为在sendall时会等待一段很小的时间,如果还有数据会一起发送,所以接收端收到的其实是一个 result_len + result 的字符串。这就称为粘包现象,要解决这个问题,在两次sendall中间加一个recv(),把两个sendall分隔开就可以了。修改后的代码:

# 发送端
conn.sendall(result_len)  # 期望作为 int 类型
conn.recv(1024)
conn.sendall(result)  # 期望作为 str 类型

# 接收端
result_len = int(str(sk.recv(1024), encoding='utf8'))
sk.send(bytes('111', encoding='utf8'))
result = str(sk.recv(1024), encoding='utf8')

参考:https://www.cnblogs.com/skyflask/articles/8283112.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值