前言
在写客户端发送文件到Java服务器的时候,一是遇到了Netty接收默认字节长度的问题,Netty默认的接收长度是1024,这个问题通过DelimiterBasedFrameDecoder解决。二是我一开始除了应用DelimiterBasedFrameDecoder之外,还应用了StringDecoder,结果在Handler里(继承自ChannelHandlerAdapter),接收到的object是String,然后通过String.getBytes()获得的字节流的长度总是和原文件的长度不同(通过notepad++查看)。随后,我把StringDecoder去掉了,只用DelimiterBasedFrameDecoder就可以了。
其中C端,我用的Windows上的零拷贝函数TransmitFile(),因为是通过字节流写文件的,所以如果通过记事本打开的文件包含中文的话会出现乱码问题。
源程序
客户端(C):
VS2019,Debug,Win32
#include <stdio.h>
#include <stdlib.h>
#include <ws2tcpip.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <MSWSock.h>
#pragma comment(lib, "Mswsock.lib ")
int sendFile()
{
const wchar_t* fileName = L"D:/writeJson.txt";
const char* ip = "127.0.0.1";
const char* port = "8080";
// 创建并初始化winsock数据变量
WSADATA wsaData = { 0 };
int iResult = 0;
SOCKET hSocket = INVALID_SOCKET;
int iFamily = AF_INET;
int iType = SOCK_STREAM;
int iProtocol = IPPROTO_TCP;
// 初始化 Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return -1;
}
// 打开socket
hSocket = socket(iFamily, iType, iProtocol);
if (hSocket == INVALID_SOCKET) {
printf("socket function failed with error = %d\n", WSAGetLastError());
WSACleanup();
return -2;
}
// 设置远程段地址
sockaddr_in remoteAddr;
remoteAddr.sin_family = AF_INET;
//remoteAddr.sin_addr.s_addr = inet_addr(v[2]);
//绑定ip
inet_pton(AF_INET, ip, &remoteAddr.sin_addr);
//绑定端口
remoteAddr.sin_port = htons(atoi(port));
do {
// 打开文件
HANDLE hFile = CreateFile((LPCWSTR)fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
iResult = -3;
printf("打开文件失败");
break;
}
// 获取文件大小
//GetFileSize(hFile, NULL);
LARGE_INTEGER liFileSize;
if (GetFileSizeEx(hFile, &liFileSize) == FALSE) {
iResult = -4;
printf("获取文件大小失败");
break;
}
// 连接到远程端
iResult = connect(hSocket, (SOCKADDR*)&remoteAddr, sizeof(remoteAddr));
if (iResult == SOCKET_ERROR) {
printf("connect function failed with error: %ld\n", WSAGetLastError());
iResult = -5;
break;
}
// 使用TransmitFile发送文件
if (TransmitFile(hSocket, hFile, 0, 0, NULL, NULL, TF_USE_DEFAULT_WORKER) == FALSE) {
printf("TransmitFile function failed with error: %ld\n", WSAGetLastError());
iResult = -6;
break;
}
} while (0);
const char* endStr = "$_$";
//发送结尾标识符
iResult = send(hSocket, endStr, strlen(endStr) + 1, 0);
if (iResult == SOCKET_ERROR) {
printf("发送结尾标记失败\n");
iResult = -10;
}
// 关闭socket
iResult = closesocket(hSocket);
if (iResult == SOCKET_ERROR) {
printf("closesocket failed with error = %d\n", WSAGetLastError());
iResult = -7;
}
// 清理
WSACleanup();
return iResult;
}
服务器端Netty接收(Java):
Netty版本:
监听端口程序HttpServer.java:
public class HttpServer {
/**
* 监听端口
*/
private static int port = 8080;
public static void main(String[] args) {
HttpServer httpServer = new HttpServer();
httpServer.acceptWait();
}
/**
* @Description : Netty监听数据请求
* @author : 申劭明
* @date : 2019/9/17 10:29
*/
public void acceptWait() {
//监听请求
EventLoopGroup listenGroup = new NioEventLoopGroup();
//请求处理
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
//绑定监听请求和处理请求的group
bootstrap.group(listenGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
//采取灵活的数据长度接收数据,入参为单位是字节,1<<4<<20表示16MB,发送信息以$_$结尾
.addLast(new DelimiterBasedFrameDecoder(1<<4<<20, Unpooled.copiedBuffer("$_$".getBytes())))
.addLast(new RequestHandler());
}
});
ChannelFuture future = null;
try {
future = bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
listenGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
自己定义的Handler,RequestHandler.java:
public class RequestHandler extends ChannelHandlerAdapter {
public RequestHandler(){
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.err.println(msg.getClass());
if (msg instanceof ByteBuf){
ByteBuf byteBuf = (ByteBuf)msg;
System.out.println(byteBuf.toString(CharsetUtil.US_ASCII).length());
if (byteBuf.toString(CharsetUtil.US_ASCII).length() < 10){
return;
}
File file = new File("D:/test.jpg");
//DMA
RandomAccessFile randomAccessFile = new RandomAccessFile(file,"rw");
//从文件开头写数据
randomAccessFile.seek(0);
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
System.out.println(bytes.length);
randomAccessFile.write(bytes);
randomAccessFile.close();
ctx.close();
}else{
return;
}
}
// @Override
// protected void messageReceived(ChannelHandlerContext channelHandlerContext, Object o) {
// //接收数据
// Request request = new Request((ByteBuf) o);
// String receiveMessage = request.getMessage();
// System.out.println(receiveMessage);
// if (receiveMessage.contains("\"wxid\":\"gh_")){
// return;
// }
// String receiveMessage = ((ByteBuf)o).toString(CharsetUtil.US_ASCII);
// System.err.println(receiveMessage);
//
// channelHandlerContext.close();
// }
public static String httpGet(String message){
HttpClient httpClient = null;
try {
URL url = new URL("http://192.168.100.100:9000/cardb/testWord?word=" + message);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestMethod("GET");
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), CharsetUtil.UTF_8));
String line = null;
StringBuilder result = new StringBuilder();
while ((line = br.readLine()) != null){
result.append(line);
}
connection.disconnect();
System.err.println(result);
JSONObject jsonObject = new JSONObject(result.toString());
return jsonObject.get("data").toString();
} catch (IOException e) {
e.printStackTrace();
} finally{
if (httpClient != null){
httpClient.closeServer();
}
}
return null;
}
private static String httpGet(Message message){
return httpGet(message.getContent());
}
}