您的位置:首页 > 编程语言 > C语言/C++

Java客户端上传图片(文件)到c++服务器

2016-09-15 11:44 696 查看
参考自:http://blog.csdn.net/nupt123456789/article/details/8047619

Java客户端上传图片(文件)到c++服务器

主要思路:将所有的数据类型都转化为byte流,对byte进行传输,c++服务器使用char数组,java使用byte数组进行图片(文件)的传输。

传输过程(socket建立连接的条件下):



C++服务器

TAG.h

#pragma once

#define _TAG_H_
#ifdef _TAG_H_

#include<Windows.h>
#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
#include <winsock2.h>
using 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<string>
#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"<<endl;
}
}
}
sockaddr_in initSockaddr_in() {
sockaddr_in sockaddr_in;
int port = 8888;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(port);//主机字节序转网络字节序
sockaddr_in.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
return sockaddr_in;
}

DWORD WINAPI SListen(LPVOID lparam) {
SOCKET socket = (SOCKET)lparam;
COMMAND command;
while (true) {
memset(&command, 0, sizeof(command));
int ret;
ret = recv(socket, (char*)&command, sizeof(command), 0);
if (ret == SOCKET_ERROR)
{
cout << "Client is close!"<<endl;
break;
}
else{
cout << "接收到命令:" << command.ID << endl;
switch (command.ID)
{
case FileInfo:
break;
case SendFile:
{
char* str = (char*)command.lparam;
cout << "原文件名" << str<< endl;
//开始接收文件
recvFile(socket);

}
break;
case StopSendFile:
break;
case SendString:
break;
default:
break;
}
}
}
return 0;
}

void recvFile(SOCKET socket) {
int ret;
char buffer[BUF_LEN];
//接收文件长度
long long fileLenght;
ret = recv(socket, (char*)&fileLenght, sizeof(fileLenght), 0);
if (ret == SOCKET_ERROR)
{
cout << "接收文件长度失败" << endl;
return;
}
cout << "文件长度为:" << fileLenght << endl;

//告诉客户端可以发送文件了
bool isOk = true;
ret = send(socket, (char*)&isOk, sizeof(isOk), 0);
if (ret == SOCKET_ERROR)
{
cout << "发送确认信息失败" << endl;
return;
}

//文件路径
char fileName[20];
string filePath;
SYSTEMTIME sys;
GetLocalTime(&sys);
sprintf_s(fileName, "%04d%02d%02d%02d%02d%02d.jpg", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond);
filePath = "F:\\Android\\";
filePath += fileName;
//打开文件
ofstream outfile;
outfile.open(filePath, ios::out| ios::binary);//传输图片使用二进制格式打开
if (!outfile) {
cout << "fail to open file!" << endl;
}
long recvTotalLen = 0;
while (recvTotalLen < fileLenght) {
memset(buffer, 0, sizeof(buffer));
ret = recv(socket, buffer, sizeof(buffer), 0);
if (ret == SOCKET_ERROR)
{
cout << "Error:" << GetLastError() << endl;
cout << "socket fail when recv file fail !" << endl;
break;
}
outfile.write(buffer, ret);
recvTotalLen += ret;
cout << "已接收:"<<(recvTotalLen*1.0 / fileLenght*1.0) * 100 << "%" << endl;
}

if (recvTotalLen == fileLenght)
{
cout << "recv file success !" << endl;
}
else
{
cout << "recv file error !" << endl;
}
outfile.close();
}


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();
}
}

}

NetDataCommand.java

package client;

public class NetDataCommand {
private static final int IDLen=4;
private static final int LparamLen=2048;
private static final int CommandLen=2052;
public byte []byteArrayData=new byte[CommandLen];
private int ID;
private String lparam;
private NetDataTypeTransform mDataTypeTransform=new NetDataTypeTransform();
public byte[] getByteArrayData(){
return byteArrayData;
}
public NetDataCommand(){

}
public NetDataCommand(int ID,String lparam) {
// TODO Auto-generated constructor stub

this.ID=ID;
this.lparam=lparam;
byte[] IDbyte = mDataTypeTransform.IntToByteArray(ID);
System.arraycopy(IDbyte,0, byteArrayData, 0, IDbyte.length);
byte[] Strbyte = mDataTypeTransform.StringToByteArray(lparam);
System.arraycopy(Strbyte,0,byteArrayData,IDbyte.length,Strbyte.length);
}
public NetDataCommand(int ID,byte[] lparam) {
// TODO Auto-generated constructor stub

byte[] IDbyte = mDataTypeTransform.IntToByteArray(ID);
System.arraycopy(IDbyte,0, byteArrayData, 0, IDbyte.length);
byte[] Strbyte =lparam;
System.arraycopy(Strbyte,0,byteArrayData,IDbyte.length,Strbyte.length);
}
public NetDataCommand(byte[] dataArray){
int id=1;
String lpString="";
System.arraycopy(dataArray,0, byteArrayData,0,CommandLen);
//提取指令id
byte[] forIntID = new byte[IDLen];
System.arraycopy(dataArray,0,forIntID,0,forIntID.length);
id=mDataTypeTransform.ByteArrayToInt(forIntID);

//提取指令内容
byte[] StrTemp=new byte[LparamLen];
System.arraycopy(dataArray,IDLen,StrTemp,0,StrTemp.length);
lpString=mDataTypeTransform.ByteArraytoString(StrTemp, StrTemp.length);
//lpString=StrTemp.toString();
ID=id;
lparam=lpString;
}
public int getID(){
return ID;
}
public String getLparam(){
return lparam;
}
public void setID(int id) {
this.ID=id;
}
public void setLparam(String str){
this.lparam=str;
}

}


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);

点击打开链接下载源代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息