首页 » 编程 » Python » Python学习 » 网络编程 » 正文

基于socket实现简单套接字通信

基于socket实现简单套接字通信

服务端:

import socket

# 1. 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2. 绑定手机卡
phone.bind(("127.0.0.1", 8080))

# 3. 开机
phone.listen(5)  # 最大挂起链接数

# 4. 等电话链接
print("等待连接中...")
conn, client_addr = phone.accept()
print("建立连接成功,客户端地址为:", client_addr)

# 5. 收,发消息
data = conn.recv(1024)  # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
print("来自客户端的数据:", data)

print("处理数据,返回给客户端...")
conn.send(data.upper())

# 6. 挂电话
conn.close()

# 7. 关机
phone.close()

启动服务端后会等待连接,如下:

等待连接中…

 

客户端:

import socket

# 1. 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2. 拨号
phone.connect(("127.0.0.1", 8080))

# 3. 发、收消息
print("发送数据 hello 给服务器端...")
phone.send("hello".encode("utf-8"))
data = phone.recv(1024)
print("服务端发过来的数据:", data)

# 4. 挂电话
phone.close()

启动客户端,会将 hello发动到服务端,服务端处理后返回回来进行打印,运行后如下:

发送数据 hello 给服务器端…
服务端发过来的数据: b’HELLO’

 

此时,服务端的运行结果如下:

等待连接中…
建立连接成功,客户端地址为: (‘127.0.0.1’, 4802)
来自客户端的数据: b’hello’
处理数据,返回给客户端…

 

加上通信循环

服务端:

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(("127.0.0.1", 8080))
phone.listen(5)

print("等待连接中...")
conn, client_addr = phone.accept()
print("建立连接成功,客户端地址为:", client_addr)

while True:
    data = conn.recv(1024)  # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
    print("来自客户端的数据:", data)
    print("处理数据,返回给客户端...")
    conn.send(data.upper())

conn.close()
phone.close()

 

客户端:

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))

while True:
    msg = input(">>>: ").strip()
    print("发送数据 hello 给服务器端...")
    phone.send(msg.encode("utf-8"))
    data = phone.recv(1024)
    print("服务端发过来的数据:", data)

phone.close()

 

运行服务端:

等待连接中…
建立连接成功,客户端地址为: (‘127.0.0.1’, 4878)
来自客户端的数据: b’hello’
处理数据,返回给客户端…
来自客户端的数据: b’world’
处理数据,返回给客户端…

 

运行客户端:

>>>: hello
发送数据 hello 给服务器端…
服务端发过来的数据: b’HELLO’
>>>: world
发送数据 hello 给服务器端…
服务端发过来的数据: b’WORLD’
>>>:

 

处理bug

以上加上循环后可能还会出现一些bug:

1. 当端口占用或在使用中时,会出现异常

2. 当客户端直接回车时,客户端会卡主。原因是应用调用send实际上是交给操作系统去发送这个空字符串,操作系统按照tcp协议去发送,由于空字符不符合协议,所以根本就没有发。

3. 当客户端直接停掉时,服务端在Windows情况下会报错,在linux情况下会死循环

针对上面的3个问题,对程序作出修改:

服务端:

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 当端口占用时,重用链接。另一个方式是修改内核参数,只限linux系统
phone.bind(("127.0.0.1", 8080))
phone.listen(5)

print("等待连接中...")
conn, client_addr = phone.accept()
print("建立连接成功,客户端地址为:", client_addr)

while True:
    try:
        data = conn.recv(1024)  # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
        if not data: break  # 如果接收到的数据是空时,表示客户端已经断掉了,直接break,适用于linux系统
        print("来自客户端的数据:", data)
        print("处理数据,返回给客户端...")
        conn.send(data.upper())
    except ConnectionResetError:  # 使用于Windows系统,利用try...except来捕捉异常
        break

conn.close()
phone.close()

 

客户端:

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))

while True:
    msg = input(">>>: ").strip()
    if not msg: continue  # 如果直接回车是空字符串,返回重新输入
    print("发送数据 hello 给服务器端...")
    phone.send(msg.encode("utf-8"))
    data = phone.recv(1024)
    print("服务端发过来的数据:", data)

phone.close()

 

链接循环

上面服务端就能实现服务一个客户端,当客户端断掉时,服务端也断掉了,显然不符合常理。我们来改一下,使服务端能够服务多个客户端,使用链接循环,实际上就是加一个while循环,这里我们还不能实现并发,后边再学,但这里可以实现多个客户端一个一个的服务,当有多个客户端连接的时候,需等待之前的客户端断掉才能够被服务。

修改服务端代码如下:

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 当端口占用时,重用链接。另一个方式是修改内核参数,只限linux系统
phone.bind(("127.0.0.1", 8080))
phone.listen(5)

while True:  # 链接循环,这里还不能实现并发,后边再学,但这里可以实现多个客户端一个一个的服务,当有多个客户端连接的时候,需等待之前的客户端断掉才能够被服务
    print("等待连接中...")
    conn, client_addr = phone.accept()
    print("建立连接成功,客户端地址为:", client_addr)

    while True:  # 通信循环
        try:
            data = conn.recv(1024)  # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
            if not data: break  # 如果接收到的数据是空时,表示客户端已经断掉了,直接break,适用于linux系统
            print("来自客户端的数据:", data)
            print("处理数据,返回给客户端...")
            conn.send(data.upper())
        except ConnectionResetError:  # 使用于Windows系统,利用try...except来捕捉异常
            break

    conn.close()
phone.close()

如何看效果,客户端拷贝多个,先启动服务端,然后启动其他的客户端,会看到服务端只能同时服务一个但是当停止一个客户端时,另一个客户端就会被服务了。

 

模式ssh执行命令

补充:

在服务器上执行命令有两种方式:使用os模块和subprocess模块,但是os模块不能满足本程序的需求。

来看看os模块:

如下,查看C盘下的文件或目录:

import os

os.system("dir C:\\")

输出结果(忽略乱码,是pychrm编码在Windows环境下的设置问题):

������ C �еľ�û�б�ǩ��
 �������� 84E2-ADD7

 C:\ ��Ŀ¼

2018/02/27  10:36            11,264 6d79d5ad-aa2d-4d41-953a-910c1dc3fcbf.bak
2017/10/25  12:41    <DIR>          AMD
2018/02/06  23:12    <DIR>          apache-tomcat-8.5.28
2017/11/14  20:54         1,797,880 APlayer.dll
2016/12/13  15:35           407,184 APlayerUI.dll
2017/11/10  20:28           926,352 DapCtrl.dll
2018/02/26  03:15    <DIR>          ffmpeg-20180226-f4709f1-win64-static
2017/10/25  12:28    <DIR>          Intel
2017/10/25  21:18    <DIR>          LenovoDrivers
2018/06/27  22:49                 0 MiFlashvcom.ini
2018/01/05  12:41    <DIR>          PerfLogs
2018/08/09  10:58    <DIR>          Program Files
2018/08/18  17:32    <DIR>          Program Files (x86)
2017/12/10  20:16    <DIR>          Python27
2018/05/20  16:49    <DIR>          Python36
2018/05/20  15:40    <DIR>          SnapPlugin
2017/10/25  12:34    <DIR>          Users
2018/08/02  23:57    <DIR>          Windows
               5 ���ļ�      3,142,680 �ֽ�
              13 ��Ŀ¼ 24,049,496,064 �����ֽ�

os.system()  只能将执行的结果打印到终端,不能够返回给其他的人,比如客户端。

 

你说给执行结果附一个值,不就可以返回了吗?如下:

import os

res = os.system("dir C:\\")
print(res)   # 实际上输出为:0 ,表示上面的命令执行成功。非0 表示执行失败

实际上是不行的。

 

所以我们要用subprocess:

服务器端改为如下:

import socket
import subprocess

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(("127.0.0.1", 8080))
phone.listen(5)

while True:
    print("等待连接...")
    conn, client_addr = phone.accept()
    print("建立连接,客户端地址为:", client_addr)

    while True:
        try:
            # 1. 接收命令
            cmd = conn.recv(1024)
            if not cmd: break

            # 2. 执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode("GBK"),  # 使用GBK解码
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout = obj.stdout.read()  # 讲对象中的数据读出来
            stderr = obj.stderr.read()  # 讲对象中的数据读出来

            # 3. 返回结果
            conn.send(stdout + stderr)  # 实际上是产出一个新的空间
        except ConnectionResetError:
            break

    conn.close()
phone.close()

 

客户端改为如下:

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))

while True:
    cmd = input(">>>: ").strip()
    if not cmd: continue
    phone.send(cmd.encode("GBK"))  # 使用GBK编码,因为我在Windows上运行
    data = phone.recv(1024)
    print(data.decode("GBK"))  # 使用GBK解码

phone.close()

 

最后运行结果如下:

>>>: dir
 驱动器 D 中的卷是 Lenovo
 卷的序列号是 D862-9EC2

 D:\PycharmProjects\python_fullstack_middle\第三模块·面向对象&网络编程基础\第2章·网络编程\02 加上循环\04 模拟ssh执行命令 的目录

2018/08/19  12:43    <DIR>          .
2018/08/19  12:43    <DIR>          ..
2018/08/19  12:42               368 客户端.py
2018/08/19  12:43             1,128 服务端.py
2018/08/19  12:28                54 补充.py
               3 个文件          1,550 字节
               2 个目录 26,375,634,944 可用字节

>>>: dir C:\\
文件名、目录名或卷标语法不正确。

>>>: dir c:\
 驱动器 C 中的卷没有标签。
 卷的序列号是 84E2-ADD7

 c:\ 的目录

2018/02/27  10:36            11,264 6d79d5ad-aa2d-4d41-953a-910c1dc3fcbf.bak
2017/10/25  12:41    <DIR>          AMD
2018/02/06  23:12    <DIR>          apache-tomcat-8.5.28
2017/11/14  20:54         1,797,880 APlayer.dll
2016/12/13  15:35           407,184 APlayerUI.dll
2017/11/10  20:28           926,352 DapCtrl.dll
2018/02/26  03:15    <DIR>          ffmpeg-20180226-f4709f1-win64-static
2017/10/25  12:28    <DIR>          Intel
2017/10/25  21:18    <DIR>          LenovoDrivers
2018/06/27  22:49                 0 MiFlashvcom.ini
2018/01/05  12:41    <DIR>          PerfLogs
2018/08/09  10:58    <DIR>          Program Files
2018/08/18  17:32    <DIR>          Program Files (x86)
2017/12/10  20:16    <DIR>          Python27
2018/05/20  16:49    <DIR>          Python36
2018/05/20  15:40    <DIR>          SnapPlugin
2017/10/25  12:34    <DIR>          Users
2018/08/02  23:57    <DIR>          Windows
               5 个文件  
>>>: 

 

赞 (0)

发表评论

*