一、实现思路
1、客户端(Java实现)
(1) 判断所要上传的图片的大小与类型
(2) 与服务器建立Socket连接
(3) 发送图片的大小与类型信息给服务器
(4) 接收服务器的确认信息,并发送图片给服务器
(5) 接收服务的回传信息
2、服务器(Python实现)
(1) 监听指定端口,接收Socket连接请求
(2) 启用多线程处理连接请求
(3) 接收客户端发送的图片大小与类型信息,并回传确认信息给客户端
(4) 接收图片并保存到本地
(5) 发送接收完毕消息给客户端
二、详细代码
客户端(Java实现)
import net.sf.json.JSONObject;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Objects;
public class SocketClient {
//允许上传的图片类型
private final HashSet<String> IMG_TYPE = new HashSet<String>() {{
add("png");
add("jpeg");
add("jpg");
}};
//允许上传的图片大小(10MB)
private final int MAX_LENGTH = 1024 * 1024 * 10;
//服务器IP地址
private final String URL = "192.168.56.1";
//服务器端口号
private final int PORT = 9877;
//图片路径
private final String ImgPath = "src/4.jpeg";
private Socket mSocket;
private OutputStream outputStream;
private InputStream inputStream;
public SocketClient() {
try {
SendImage();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
//发送图片到服务器
public void SendImage() throws IOException, InterruptedException {
File file = new File(ImgPath);
//图片长度
long imgLength = file.length();
//图片类型
String imgType = Files.probeContentType(file.toPath()).split("/")[1];
System.out.println("图片大小:" + imgLength + "B;图片类型:" + imgType);
//对文件大小及类型进行限制判断
if (imgLength > MAX_LENGTH) {
System.out.println("图片超出最大限制,请重新选择一张图片!");
return;
} else if (!IMG_TYPE.contains(imgType)) {
System.out.println("文件类型不符合要求,请重新选择一张图片!");
return;
}
//建立Socket连接
mSocket = new Socket(InetAddress.getByName(URL), PORT);
System.out.println("与服务器建立连接成功!");
//获取socket输出流与输入流
outputStream = mSocket.getOutputStream();
inputStream = mSocket.getInputStream();
//发送图片详细信息给服务器
//使用JSON格式对数据进行封装
JSONObject jsonObject = new JSONObject();
jsonObject.put("Length", imgLength);
jsonObject.put("FileType", imgType);
//将JSON格式数据转换成字节数组,使用UTF-8编码
byte[] sendMessage = jsonObject.toString().getBytes(StandardCharsets.UTF_8);
//发送字节数组
outputStream.write(sendMessage);
//接收服务器端发送过来的确认数据
System.out.println("接收服务器的确认数据中......");
String confirmData = readMessage();
System.out.println("服务器确认信息:" + confirmData);
if (!Objects.equals(confirmData, "OK")) {
System.out.println("发送给服务器的图片信息错误");
return;
}
System.out.println("接收确认消息完成......");
//发送图片------------------
//获取图片的缓冲输入流
System.out.println("图片上传中......");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
int readLen;
byte[] buf = new byte[1024];
while ((readLen = bis.read(buf)) != -1) {
outputStream.write(buf, 0, readLen);
}
mSocket.shutdownOutput();
System.out.println("图片上传完成......");
//接收来自服务端的回复
String result = readMessage();
System.out.println("服务器回传消息:" + result);
//关闭图片缓冲输入流
bis.close();
//关闭socket输入流
inputStream.close();
//关闭socket输出流
outputStream.close();
//断开socket连接
mSocket.close();
}
//读取服务器发送过来的消息
private String readMessage() {
try {
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
return new String(bytes, 0, len);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
new SocketClient();
}
}
服务器(Python实现)
import socket
import time
import json
from threading import Thread
# 服务器主机地址和监听的端口号
HOST = socket.gethostname()
PORT = 9877
# 服务端回应消息
def response_message():
message = 'I have receive your image'
return message.encode("utf-8")
# 接收图片数据
def recv_img(client_sock):
data = b''
print("开始接收客户端数据--------")
try:
confirm_data = client_sock.recv(1024)
print(confirm_data.decode('utf-8'))
json_data = json.loads(confirm_data)
img_type = json_data['FileType']
img_length = json_data['Length']
client_sock.send('OK'.encode('utf-8'))
print('发送确认消息完成--------')
except KeyError as a:
print("图片信息数据错误:" + str(a))
client_sock.send('ERROR')
return
print("开始接收图片数据---------------")
while len(data) < img_length:
try:
recv_data = client_sock.recv(1024000)
time.sleep(0.1)
data += recv_data
except Exception as e:
print("Socket接收数据错误:" + str(e))
break
if len(data) != 0:
save_image(data, img_type)
else:
print("Client Close!")
# 将传输过来的图片保存到img目录下
def save_image(imageData, imgType):
_time = time.strftime("%Y%m%d_%H%M%S", time.localtime())
imageSavePath = "img/" + _time + "." + imgType
print(imageSavePath)
file = open(imageSavePath, 'wb+')
file.write(imageData)
file.close()
# 处理客户端请求过来的数据
def accept_data(client_sock, client_addr):
recv_img(client_sock)
# 生成回复消息
response = response_message()
# 发送服务器回应消息
client_sock.send(response)
# 关闭与客户端的socket连接
client_sock.shutdown(1)
client_sock.close()
# Socket 服务器类
class SocketServer:
def __init__(self, host=HOST, port=PORT):
self.sock = None
self.host = host
self.port = port
self.setup_socket()
self.accept()
self.shout_down_socket()
# 建立Socket连接
def setup_socket(self):
# 创建 socket 对象
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口号
self.sock.bind((self.host, self.port))
# 设置最大连接数,超过后排队
self.sock.listen(600)
# 使用多线程接受客户端的请求
def accept(self):
while True:
# address ----> 消息发送方ip地址
# client ----> 服务器与客户端之间的Socket连接
(client, address) = self.sock.accept()
# target ----> 线程执行的方法 ; args ----> target目标调用的参数元组
th = Thread(target=accept_data, args=(client, address))
th.start()
# 关闭Socket连接
def shout_down_socket(self):
if self.sock is not None:
self.sock.shutdown()
self.sock.close()
if __name__ == "__main__":
SocketServer()
客户端Java实现需要导入相关的JSON数据格式的依赖包,依赖包可以在我的Github库中下载
https://github.com/Hunter957/ImageUploadServerhttps://github.com/Hunter957/ImageUploadServer
三、实现效果
客户端与服务器的Socket通信没有太大问题,图片也没有出现数据丢失的情况