socket编程

1简单的socket server和client,完成一次通讯

import socket


server = socket.socket()#创建server实例
server.bind(("localhost",9999))#绑定server IP和Port
server.listen()#监听

conn,addr = server.accept()#接收链接
data = conn.recv(1024)#接收数据,赋值给data变量
print(data)#打印接收到的数据
conn.send(((data.decode()).upper()).encode("utf-8"))#把接收到的内容转换为大写,发回给客户端

server.close()#关闭server
socket_server
import socket

client = socket.socket()#创建客户端实例
client.connect(("localhost",9999))#链接socket server的IP和Port
data = input(">>:")#输入传输的内容
client.send(data.encode("utf-8"))#把输入的内容编码为二进制
recived_data = client.recv(1024)#接收服务器传回来的数据
print(recived_data.decode())#打印解码后的数据

client.close()#关闭客户端
socket_client

 

上面是最简单的socket收发数据的例子,客户端发送一次字符串,服务器接收字符串后,把字符串转换为大写后发回给客户端,客户端接收后,打印结果。这个例子收发都是一次

2 多次收发数据

import socket


server = socket.socket()#创建server实例
server.bind(("localhost",9999))#绑定server IP和Port
server.listen()#监听

conn,addr = server.accept()#创建与客户端的链接

"""accept() -> (socket object, address info)

        Wait for an incoming connection.  Return a new socket
        representing the connection, and the address of the client.
        For IP sockets, the address info is a pair (hostaddr, port).
        """

#conn,addr不能放在循环里,如果放在循环里表示每次循环都要重新创建server和客户端的链接,这样就不能多次接收同一个客户端的数据了
while True:
    data = conn.recv(1024)#接收数据,赋值给data变量
    print(data)#打印接收到的数据
    conn.send(((data.decode()).upper()).encode("utf-8"))#把接收到的内容转换为大写,发回给客户端

server.close()#关闭server
socket_server
import socket

client = socket.socket()#创建客户端实例
client.connect(("localhost",9999))#链接socket server的IP和Port

while True:
    data = input(">>:")#输入传输的内容
    client.send(data.encode("utf-8"))#把输入的内容编码为二进制
    recived_data = client.recv(1024)#接收服务器传回来的数据
    print(recived_data.decode())#打印解码后的数据

client.close()#关闭客户端
socket_client

当client发送空字符是,程序会hang,这个需要解决

在客户端输入信息后加一个判断,如果是空字符就结束本次循环,继续输入

import socket

client = socket.socket()#创建客户端实例
client.connect(("localhost",9999))#链接socket server的IP和Port

while True:
    data = input(">>:")#输入传输的内容


    if len(data) == 0 :continue#加入判断


    client.send(data.encode("utf-8"))#把输入的内容编码为二进制
    recived_data = client.recv(1024)#接收服务器传回来的数据
    print(recived_data.decode())#打印解码后的数据

client.close()#关闭客户端

当客户端退出时,server端也会同时退出,这个貌似和操作系统有关。

如果要实现多人同时通信该怎么办呢?这个问题我们先放一放,先干点别的。

 
SSH模型

使用socket编写一个简单ssh试试

import socket
import os   


server = socket.socket()#创建server实例
server.bind(("localhost",9999))#绑定server IP和Port
server.listen()#监听

while True:
    conn,addr = server.accept()#创建与客户端的链接

    #conn,addr不能放在循环里,如果放在循环里表示每次循环都要重新创建server和客户端的链接,这样就不能多次接收同一个客户端的数据了
    while True:
        data = conn.recv(1024)#接收数据,赋值给data变量
        print(data)#打印接收到的数据

        result = os.popen(data.decode("utf-8")).read() #执行客户端的命令,把命令结果赋值

        if not data:
            print("对方断了......")
            break
        conn.send(str(result).encode("utf-8"))

server.close()#关闭server
ssh_socket_server
import socket

client = socket.socket()#创建客户端实例
client.connect(("localhost",9999))#链接socket server的IP和Port

while True:
    cmd = input(">>:")#输入传输的内容
    if len(cmd) == 0 :continue
    client.send(cmd.encode("utf-8"))#把输入的内容编码为二进制
    recived_data = client.recv(1024)#接收服务器传回来的数据
    print(recived_data.decode("utf-8"))#打印解码后的数据

client.close()#关闭客户端
ssh_socket_client

执行ipconfig命令接收命令的结果,我发现客户端不能一次接收所有的数据,需要手动多次接收才能把所有的数据接收过来,这是由于client.recv(1024)设置了一次接收操作就接收1024字节,但是实际要接受的数据大于1024,因此需要多次接收。

我们可以通过增加每次接收的字节数解决这个问题,我把每次接收字节数改为102400(100KB),这样可以一次接收ipconfig命令的执行结果。这样看,貌似解决了问题,实际上,如果要传输的数据有1000GB的话,我们不能无限的扩大每次要接受的字节数,而且网络硬件也有这个字节数的限制,这个限制是不容易修改的。所以,想要解决这个问题,还需要在想想。

我们修改一下代码

import socket
import os


server = socket.socket()#创建server实例
server.bind(("localhost",9999))#绑定server IP和Port
server.listen()#监听

while True:
    conn,addr = server.accept()#创建与客户端的链接

    #conn,addr不能放在循环里,如果放在循环里表示每次循环都要重新创建server和客户端的链接,这样就不能多次接收同一个客户端的数据了
    while True:
        cmd = conn.recv(1024)#接收数据,赋值给data变量
        print(cmd)#打印接收到的数据

        cmd_result = os.popen(cmd.decode("utf-8")).read() #执行客户端的命令,把命令结果赋值
        cmd_result_size = len(cmd_result.encode("utf-8"))#先把命令结果编码,然后计算长度,把长度赋值
        print(cmd_result_size)
        if not cmd:
            print("对方断了......")
            break
        conn.send(str(cmd_result_size).encode("utf-8"))#发送结果长度
        conn.send(str(cmd_result).encode("utf-8"))#发送数据

server.close()#关闭server
ssh_socket_server
import socket

client = socket.socket()#创建客户端实例
client.connect(("localhost",9999))#链接socket server的IP和Port

while True:
    cmd = input(">>:")#输入传输的内容
    if len(cmd) == 0 :continue
    client.send(cmd.encode("utf-8"))#把输入的内容编码为二进制
    cmd_result_size = int(client.recv(1024))#接收命令结果过长度
    print(cmd_result_size)
    recived_size = 0
    recived_data = b''
    while recived_size < cmd_result_size:#接收到的数据小于命令结果大小时,接收数据
        data = client.recv(1024)#接收服务器传回来的数据
        recived_size += len(data) #计算接收到数据的大小
        recived_data += data #接收数据
        print(recived_size)
    print(len(recived_data))
    print(recived_data.decode("utf-8"))#打印解码后的数据

client.close()#关闭客户端
ssh_socket_client

 

 

代码修改后,可以完成接收大于单次传输大小限制的文件了。哦也

粘包

注意一下下面的代码

conn.send(str(cmd_result_size).encode("utf-8"))#发送结果长度
conn.send(str(cmd_result).encode("utf-8"))#发送数据

程序在处理上面两行代码的时候,可能把两次发送的内容合并在一起,一次发送给客户端,从而导致程序出错,这个现象被称为粘包。粘豆包....???......!!!!

在数据传输的时候可能会出现粘包的情况,修改代码解决这个问题

import socket
import os


server = socket.socket()#创建server实例
server.bind(("localhost",9999))#绑定server IP和Port
server.listen()#监听

while True:
    conn,addr = server.accept()#创建与客户端的链接

    #conn,addr不能放在循环里,如果放在循环里表示每次循环都要重新创建server和客户端的链接,这样就不能多次接收同一个客户端的数据了
    while True:
        cmd = conn.recv(1024)#接收数据,赋值给data变量
        print(cmd)#打印接收到的数据

        cmd_result = os.popen(cmd.decode("utf-8")).read() #执行客户端的命令,把命令结果赋值
        cmd_result_size = len(cmd_result.encode("utf-8"))#先把命令结果编码,然后计算长度,把长度赋值
        print(cmd_result_size)
        if not cmd:
            print("对方断了......")
            break
        conn.send(str(cmd_result_size).encode("utf-8"))#发送结果长度
        client_ack = conn.recv(1024)
        print(client_ack.decode())#等待client接收长度后的应答
        print("发送数据")
        conn.send(str(cmd_result).encode("utf-8"))#发送数据

server.close()#关闭server
ssh_socket_server
import socket

client = socket.socket()#创建客户端实例
client.connect(("localhost",9999))#链接socket server的IP和Port

while True:
    cmd = input(">>:")#输入传输的内容
    if len(cmd) == 0 :continue
    client.send(cmd.encode("utf-8"))#把输入的内容编码为二进制
    cmd_result_size = int(client.recv(1024))#接收命令结果过长度
    client.send('收到大小了,开始发数据吧'.encode("utf-8"))#给server端发送一个接收到数据长度的应答
    print(cmd_result_size)
    recived_size = 0
    recived_data = b''
    while recived_size < cmd_result_size:#接收到的数据小于命令结果大小时,接收数据
        data = client.recv(1024)#接收服务器传回来的数据
        recived_size += len(data) #计算接收到数据的大小
        recived_data += data #接收数据
        print(recived_size)
    #print(len(recived_data))
    print(recived_data.decode("utf-8"))#打印解码后的数据

client.close()#关闭客户端
ssh_socket_client

 

 

客户端在收到文件大小后,发送一个接受完成的应答给服务器端,服务器端在接收到这个应答后,在开始发送数据。这样就不会粘包了。

 

上面是一个简单的ssh模型

 

FTP模型

我们在试试编一个简单的FTP程序

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-11-08 16:02  goldtree358  阅读(124)  评论(0)    收藏  举报