day7-通过socket实现处理多个连接

简述

现在我们是发送和接收一次,如果想要重复发送和接收多次,该如何做呢?

发送和接收中文

客户端代码

import socket  #导入socket
#客户端

#声明socket类型,同时生成socket连接对象(实例)
client = socket.socket()
#连接远程机器
client.connect(("localhost",6969))
#发送数据
client.send("我要下载a电影".encode("utf-8"))
#接收服务器端数据
data = client.recv(1024)
print("recv:",data.decode())
#关闭连接
client.close()

#运行输出
recv: 我要下载A电影

服务端代码

import socket
#服务器端
server = socket.socket()
#绑定要监听的IP地址和端口
server.bind(("localhost",6969))
#监听
server.listen()
print("我要接电话了")

#conn就是客户端连过来而在服务器端为其生成的一个连接实例
conn,addr = server.accept()
print(conn,addr)
print("电话来了")
#接收客户端发来的数据
data = conn.recv(1024)
print("recv:",data)
#发送数据到客户端
conn.send(data.upper())
#关闭服务器端
server.close()

#运行输出
我要接电话了
<socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 50662)> ('127.0.0.1', 50662)
电话来了
recv: b'\xe6\x88\x91\xe8\xa6\x81\xe4\xb8\x8b\xe8\xbd\xbda\xe7\x94\xb5\xe5\xbd\xb1'

解析:客户端发送中文前需要将字符串encode为字节bytes,否则将会报错:bytes can only contain ASCII literal characters,因为b"test string"可以直接产生bytes类型,但需要注意的是,这个语法只适用于英文字符,如果想把它应用到中文字符上,是会产生异常的。

以上是我们只发送和接收一次,如何重复发送和接收多次?

重复发送多次和接收

服务器端代码

import socket
#服务器端
server = socket.socket()
#绑定要监听的IP地址和端口
server.bind(("localhost",6969))
#监听
server.listen()
print("我要接电话了")
while True:
    #conn就是客户端连过来而在服务器端为其生成的一个连接实例
    conn,addr = server.accept()
    print(conn,addr)
    print("电话来了")
    #接收客户端发来的数据
    data = conn.recv(1024)
    print("recv:",data)
    #发送数据到客户端
    conn.send(data.upper())
#关闭服务器端
server.close()

#运行输出
我要接电话了
<socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 51192)> ('127.0.0.1', 51192)
电话来了
recv: b'\xe4\xbd\xa0\xe5\xa5\xbd'
<socket.socket fd=244, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 51195)> ('127.0.0.1', 51195)
电话来了
recv: b'abc'

 客户端代码

import socket  #导入socket
#客户端

#声明socket类型,同时生成socket连接对象(实例)
client = socket.socket()
#连接远程机器
client.connect(("localhost",6969))

while True:
    msg = input(">>:")
    #发送数据
    client.send(msg.encode("utf-8"))  #发送中文需要encode为bytes
    #接收服务器端数据
    data = client.recv(1024)
    print("recv:",data.decode())
#关闭连接
client.close()

#运行输出(需要输入)
session 1:
>>:你好
recv: 你好
>>:你好a
Traceback (most recent call last):
  File "C:/Users/huwei/PycharmProjects/python/module_3/socket_client.py", line 14, in <module>
    data = client.recv(1024)
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。

session 2:
>>:abc
recv: ABC
>>:a

解析:在服务器端和客户端都加了while True循环以后,同样只能发送和接收一次,这是为什么?

解决:修改服务端代码,只将发送和接收放入while循环

import socket

server = socket.socket()
server.bind(("localhost",6969))
server.listen()
print("我要接电话了")

conn, addr = server.accept()
print(conn, addr)
print("电话来了")
while True:
    data = conn.recv(1024)
    print("recv:",data)
    conn.send(data.upper())
server.close()

#服务器端运行输出
我要接电话了
<socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 51240)> ('127.0.0.1', 51240)
电话来了
recv: b'abc'
recv: b'\xe4\xbd\xa0\xe5\xa5\xbd'

----------------------------------------------------------------
#客户端运行输出
>>:abc
recv: ABC
>>:你好
recv: 你好
>>:hi
recv: HI
>>:               #等待输入

解析:服务器端这里不能将while循环放在conn,addr = server.accept()前面,因为服务器端每接收和发送一次到客户端的数据,在当前会话还没有断开的情况下,又重新进行下一个实例的连接等待,下一个客户端实例连入server循环一次数据,服务器端又重新进行下一个实例的连接等待,周而复始,所以当前服务器的实例和当前客户端会卡住。

处理多个连接

上面我们实现了客户端和服务器端的多次发送和接收数据,那么我们如何在当前客户端断开以后,另一个客户端进行对服务器端的连接,按照思路,我们将客户端的连接断开,让第二个用户进行连接。

改变服务器端配置:添加了循环超过10次自动跳出程序

import socket
server = socket.socket()
server.bind(("localhost",6969)) #绑定要监听的端口
server.listen()  #监听

print("我要开始等电话了")
conn,addr = server.accept()  #等电话打进来
#conn就是客户端连过来而在服务器端为其生成的一个连接实例
print(conn,addr)
count = 0
while True:
    print("电话来了")
    data = conn.recv(1024)
    print("recv:",data.decode())

    conn.send(data.upper())
    count+=1
    if count > 10:
          break

server.close()

实验结果:

解析:当客户端被人工断开以后,服务器端进入死循环接收客户端发来的空数据,直到满足if条件句break中断为止。针对此种情况,我们可以在服务器端增加条件,当服务器端接收为空时,断开连接,代表客户端已经断开。

针对上面出现的问题,我们原本是想让客户端断开,而服务器端不进入死循环,等待下一个客户端的重新连接,如何实现客户端断开以后,服务器端等待下一次的连接呢?

因为服务器端在accept()处等待连接实例,所以我们在accept()前面再加一个while循环

import socket
server = socket.socket()
server.bind(("localhost",6969)) #绑定要监听的端口
server.listen()  #监听
while True:
    print("我要开始等电话了")
    conn,addr = server.accept()  #等电话打进来
    #conn就是客户端连过来而在服务器端为其生成的一个连接实例
    print(conn,addr)
    while True:
        print("电话来了")
        data = conn.recv(1024)
        print("recv:",data.decode())

        conn.send(data.upper())
        if not data:
            print("client has been lost....")
            break

server.close()

实验结果:

解析:当服务器端运行后,客户端1运行后可以和其交互,当客户端2也运行后,服务端不能接收数据,客户端2也无法收到服务器端返回的数据,但是客户端1可以正常交互

尝试一:将客户端1断开

解析:此时,当客户端1断开后,服务器端接收到了客户端2发送的数据,而客户端2此时也接收到了之前发送的数据,继续输入数据也可以返回。说明此前客户端2发送的连接服务器将其置于挂起状态。当客户端1断开后,客户端2可以正常连接。

模拟SSH访问

客户端

import socket

#客户端
client = socket.socket()  #声明socket类型,同时生成socket连接对象
client.connect(("127.0.0.1",6969))

while True:
    msg = input(">>:")
    if len(msg) == 0:
        continue
    client.send(msg.encode("utf-8"))
    data = client.recv(1024)  #收1024字节
    print(data.decode())

client.close()

服务端

import os
import socket
server = socket.socket()
server.bind(("127.0.0.1",6969)) #绑定要监听的端口
server.listen(5)  #监听

print("我要开始等电话了")
conn,addr = server.accept()  #等电话打进来
#conn就是客户端连过来而在服务器端为其生成的一个连接实例
print(conn,addr)

while True:
    print("电话来了")
    data = conn.recv(1024)
    print("recv:",data.decode())
    if not data:
       print("Client has been lost.....")
       break

    res = os.popen(data.decode()).read()
    conn.send(res.encode("utf-8"))

server.close()

实验结果:

注意:当客户端接收数据有限制时,如果服务端发送的数据超过了客户端接收数据的最大值,客户端只能接收设定值大小,其余将存放在缓冲区,下一次服务端再发送数据的时候,是将缓冲区里面的数据发送完以后,再发送新的返回数据。

posted @ 2017-11-07 14:40  Mr.hu  阅读(1348)  评论(1编辑  收藏  举报