Java客戶端上傳圖片(文件)到c++服務器
主要思路:將所有的數據類型都轉化為byte流,對byte進行傳輸,c++服務器使用char數組,java使用byte數組進行圖片(文件)的傳輸。
傳輸過程(socket建立連接的條件下):
C++服務器
TAG.h
#pragma once #define _TAG_H_ #ifdef _TAG_H_ #include#define BUF_LEN 2048 #define SendFile 0x01 #define StopSendFile 0x02 #define SendString 0x03 #define FileInfo 0x04 typedef struct { int ID; BYTE lparam[BUF_LEN]; }COMMAND; typedef struct { long fileLenght; char fileName[MAX_PATH]; }FILEINFO; #endif // _TAG_H_
SocketHead.h
#pragma once #includeusing namespace std; #pragma comment(lib, "WS2_32") // 鏈接到WS2_32.lib class CInitSock { public: CInitSock(BYTE minorVer = 2, BYTE majorVer = 2) { // 初始化WS2_32.dll WSADATA wsaData; WORD sockVersion = MAKEWORD(minorVer, majorVer); if (::WSAStartup(sockVersion, &wsaData) != 0) { exit(0); } } ~CInitSock() { ::WSACleanup(); } };
CPlusServer.cpp
// CPlusServer.cpp : 定義控制台應用程序的入口點。 // /* author:chenjianrun time: 2016-9-15 city: zhuhai */ #include "stdafx.h" #include"SocketHead.h" #include "iostream" #include "string.h" #include "fstream" #include#include"TAG.h" using namespace std; //初始化結構體SOCKADDR_IN sockaddr_in initSockaddr_in(); //監聽線程 DWORD WINAPI SListen(LPVOID lparam); //接收文件 void recvFile(SOCKET socket); //初始化socket void InitSocket(); CInitSock initsocket; int main() { InitSocket(); return 0; } void InitSocket() { SOCKET serverSocket; SOCKET listenSocket; sockaddr_in serveraddr = initSockaddr_in(); sockaddr_in clientaddr; //創建socket serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //進行綁定 bind(serverSocket, (sockaddr*)&serveraddr, sizeof(serveraddr)); //進行監聽 listen(serverSocket, 5); cout << "server start" << endl; int len = sizeof(clientaddr); while (true) { if (listenSocket = accept(serverSocket, (sockaddr*)&clientaddr, &len)); { //啟動會話線程 CreateThread(NULL, NULL, SListen, (LPVOID)listenSocket, NULL, NULL); cout << "CreateThread is start"<
Java客戶端
Client.java
/* author:chenjianrun time: 2016-9-15 city: zhuhai */ package client; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class Client { private static NetDataTypeTransform transform1; private static DataInputStream fromServerStream; private static DataOutputStream toServerStream; private static NetDataCommand dataCommand; private static DataFileInfo dataFileInfo; public static void main(String[] args) { try { Socket socket = new Socket("localhost",8888); toServerStream = new DataOutputStream(socket.getOutputStream()); //發送要發送文件的指令 int id = 1; String tempStr = "4.jpg"; dataCommand = new NetDataCommand(id,tempStr); toServerStream.write(dataCommand.getByteArrayData()); toServerStream.flush(); //啟動發送文件線程 SendFileThread sendFileThread = new SendFileThread(socket); sendFileThread.start(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }NetDataTypeTransform.java
package client; import java.io.UnsupportedEncodingException; public class NetDataTypeTransform { public static final String coding="GB2312"; //全局定義,以適應系統其他部分 public NetDataTypeTransform(){ } /** * 將int、long轉為低字節在前,高字節在後的byte數組 */ public byte[] IntToByteArray(int n) { byte[] b = new byte[4]; b[0] = (byte) (n & 0xff); b[1] = (byte) (n >> 8 & 0xff); b[2] = (byte) (n >> 16 & 0xff); b[3] = (byte) (n >> 24 & 0xff); return b; } public byte[] LongToByteArray(long n) { byte[] b = new byte[8]; b[0] = (byte) (n & 0xff); b[1] = (byte) (n >> 8 & 0xff); b[2] = (byte) (n >> 16 & 0xff); b[3] = (byte) (n >> 24 & 0xff); b[4] = (byte) (n >> 32 & 0xff); b[5] = (byte) (n >> 40 & 0xff); b[6] = (byte) (n >> 48 & 0xff); b[7] = (byte) (n >> 56 & 0xff); return b; } /** * byte數組轉化為int、long * 將低字節在前轉為int、long,高字節在後的byte數組 */ public int ByteArrayToInt(byte[] bArr) { if(bArr.length!=4){ return -1; } return (int) ((((bArr[3] & 0xff) << 24) | ((bArr[2] & 0xff) << 16) | ((bArr[1] & 0xff) << 8) | ((bArr[0] & 0xff) << 0))); } public long ByteArrayToLong(byte[] bArr) { if(bArr.length!=8){ return -1; } return (long) ((((bArr[7] & 0xff) << 56) | ((bArr[6] & 0xff) << 48) | ((bArr[5] & 0xff) << 40) | ((bArr[4] & 0xff) << 32) | ((bArr[3] & 0xff) << 24) | ((bArr[2] & 0xff) << 16) | ((bArr[1] & 0xff) << 8) | ((bArr[0] & 0xff) << 0))); } /** * 將byte數組轉化成String,為了支持中文,轉化時用GBK編碼方式 */ public String ByteArraytoString(byte[] valArr,int maxLen) { String result=null; int index = 0; while(index < valArr.length && index < maxLen) { if(valArr[index] == 0) { break; } index++; } byte[] temp = new byte[index]; System.arraycopy(valArr, 0, temp, 0, index); try { result= new String(temp,"GBK"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } /** * 將String轉化為byte,為了支持中文,轉化時用GBK編碼方式 */ public byte[] StringToByteArray(String str){ byte[] temp = null; try { temp = str.getBytes("GBK"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return temp; } }SendFileThread.java
package client; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.Socket; public class SendFileThread extends Thread implements Runnable{ private Socket socket; private NetDataCommand dataCommand; private NetDataTypeTransform transform = new NetDataTypeTransform(); public SendFileThread(Socket socket) { this.socket = socket; } @Override public void run() { try { DataInputStream fromServerStream = new DataInputStream(socket.getInputStream()); DataOutputStream toServerStream = new DataOutputStream(socket.getOutputStream()); //初始化文件信息 File file = new File("F:\\Android\\4.jpg"); FileInputStream fileInputStream = new FileInputStream(file); int fileLen = (int)file.length(); //1.發送文件的長度 //toServerStream.write(transform.IntToByteArray(fileLen)); toServerStream.write(transform.LongToByteArray(file.length())); toServerStream.flush(); //2.確認服務器端已經做好准備 Boolean isOk; isOk = fromServerStream.readBoolean(); System.out.println(isOk); byte[] toServerByte = new byte[2048]; int len;//每次發送長度 long sendLen=0;//已經發送的總長度 while((len = fileInputStream.read(toServerByte))>0){ toServerStream.write(toServerByte,0,len); toServerStream.flush(); sendLen+=len; System.out.println("len = "+len); System.out.println("發送進度:"+sendLen*1.0/fileLen*1.0*100+"%"); } //System.out.println(toServerByte.length); sleep(10); if (sendLen == fileLen) { System.out.println("success"); } System.out.println("send over!"); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }作為菜鳥,在寫這個東西的時候遇到了很多問題,我把遇到問題的都寫下來了,相信這些是很多新手都可能會遇到的問題
問題1:
在使用txt文件進行測試傳輸時,文件出現了亂碼的情況
分析:
在c++的輸出中outfile.write(buffer, ret)寫文件和outfile <<寫文件是有區別的,outfile.write(buffer, ret)通過形參控制可以寫一個數據塊,比如結構體、數組什麼的;而
outfile << 只能寫基本類型,如int、float、string;如果是outfile <<來寫一個數據塊的話,那麼就會出現問題。
解決方法:
將原先使用outfile << buffer;語句寫文件修改為outfile.write(buffer, ret)進行寫文件。
問題2:
C++服務器接收文件完畢之後,接收到的文件大小大於java客戶端發送過來的文件長度
分析:
我們不管是在c++還是java中,使用socket發送數據時,我們指定發送的長度應該是發送數據的實際長度,而不是buffer的字節數。
解決方法:
在java客戶端中,修改前,我的發送文件時指定的發送長度為toServerByte.length
while((len = fileInputStream.read(toServerByte))>0){
toServerStream.write(toServerByte,0,toServerByte.length);
toServerStream.flush();
}
在java客戶端中,修改後,發送文件時指定長度為len
while((len = fileInputStream.read(toServerByte))>0){
toServerStream.write(toServerByte,0,len);
toServerStream.flush();
}
問題3:
接收到的文件總莫名的多了4個byte,致使接收的圖片不能正常打開。
分析:
c++中long類型是4個byte的,而java中long是8個byte的,每次java中發送的文件長度是long類型,有8個byte,c++服務器接收文件長度時只是從socket緩沖區中copy了4個byte,未被copy的4個byte還放在緩沖區中,因此下次調用recv()函數時就會將其和文件內容一起copy到buffer中寫入文件,也就導致了文件多出4個byte,出現異常。
解決方法:
在c++服務器中將long類型的fileLenght改為long long
未改前
long fileLenght;
ret = recv(socket, (char*)&fileLenght, sizeof(fileLenght), 0);
修改後
long long fileLenght;
ret = recv(socket, (char*)&fileLenght, sizeof(fileLenght), 0);
問題4:
C++服務器中文件全部接收完畢,但是圖片不能打開
分析:
圖片是二進制文件,如果我們是已普通的文件格式打開文件,那麼我們的圖片將不能正常的打開。
解決方法:
以二進制文件格式打開文件
ofstream outfile;
//傳輸圖片使用二進制格式打開
outfile.open(filePath, ios::out| ios::binary);