前言
在寫客戶端發送文件到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());
}
}