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

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收发数据的例子,客户端发送一次字符串,服务器接收字符串后,把字符串转换为大写后发回给客户端,客户端接收后,打印结果。这个例子收发都是一次
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

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()#关闭客户端
当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

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()#关闭客户端
执行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

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()#关闭客户端
代码修改后,可以完成接收大于单次传输大小限制的文件了。哦也
粘包
注意一下下面的代码
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

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模型
FTP模型
我们在试试编一个简单的FTP程序