C++程序与Java程序网络传输文件测试

应用层不管用的是什么语言,在网络传输层都是遵循相同的协议(TCP/UDP......)。本文通过一个小例子测试了在同一台机器上,C++程序和Java程序之间传输大文件。Java程序作为服务器,监听本地端口号:12345。C++程序作为客户端,连接上服务器后发送传输文件请求,服务器接收请求后把一个大文件发送给客户端。(使用TCP协议)

      数据包包头部分的定义特别重要,由于这里不考虑那么复杂,每个数据包的第一个字节定义为不同的请求类型,如下:

#pragma once



enum{
	C2S_FILE = 1,
};

enum{
	S2C_FILE = 1,
};

enum {
	S2C_FILE_INFO = 1,
	S2C_FILE_BUFF,
	S2C_FILE_ERROR,
};

enum{
	C2S_FILE_ASK = 1,
	C2S_FILE_READY,
};

struct FileInfo
{
	char	szName[MAX_PATH];
	UINT	nSize;
};
C2S_是客户端发送给服务器的请求标识,S2C_则是服务器返回给客户端的标识。不同的标识,其对应的后面的数据传输结构也不一样。


Java代码

public class socketserver {
	public static final int C2S_FILE = 0x01;
	public static final int C2S_FILE_ASK = 1;
	public static final int C2S_FILE_READY = 2;
	
	
	public static final int S2C_FILE = 1;
	public static final int S2C_FILE_INFO = 1;
	public static final int S2C_FILE_BUFF = 2;
	public static final int S2C_FILE_END = 3;
	
	public static void main(String arv[]){
		
		String str = "123";
		str += 4 + 5;
		
		System.out.println("Hello world!");
		ServerSocket server = null;
		Socket client = null;
		InputStream is = null;
		OutputStream os = null;
		FileInputStream fis = null;
		try {
			server = new ServerSocket(12345);
			//InetSocketAddress addr = new InetSocketAddress(12345);
			client = server.accept();
			is = client.getInputStream();
			os = client.getOutputStream();
			int nSize = 1024, nSendSize = 1024+100;
			byte szBuffer[] = new byte[nSize];
			byte szSendBuffer[] = new byte[nSendSize];
			int nRead = 0;
			byte bHeader[] = new byte[6];
			bHeader[0] = (byte)S2C_FILE;
			bHeader[1] = (byte)S2C_FILE_INFO;
			
			
			byte szFile[] = new  byte[264];
			String strFile = new String("D:\\CefCode.zip");
			String strName = new String("CefCode.zip");
			File file = new File(strFile);
			int nFileSize = (int)(file.length());
			fis = new FileInputStream(file);
			boolean bFinish = false;
			while( !bFinish ){
				nRead = is.read(szBuffer, 0, nSize);
				if ( nRead == -1 )
					break;
				System.out.println("接收到" + nRead + "个字节的数据");
				int nFlag = (int)szBuffer[0];
				int nStatus = (int)szBuffer[1];
				switch( nFlag )
				{
				case C2S_FILE: {// 客户端请求文件
					if ( nStatus == C2S_FILE_ASK ){
						System.arraycopy(strName.getBytes(), 0, szFile, 0, strName.length());
						byte b[] = IntToByteArray(nFileSize);
						System.arraycopy(b, 0, szFile, 260, 4);
						
						System.arraycopy(bHeader, 0, szSendBuffer, 0, 6);
						System.arraycopy(szFile, 0, szSendBuffer, 6, 264);
						os.write(szSendBuffer, 0, 270);
						break;
					}
					if ( nStatus == C2S_FILE_READY ){
						int nReadBytes;
						bHeader[1] = (byte)S2C_FILE_BUFF;
						byte bFileBuff[] = new byte[1024];
						while( ( nReadBytes = fis.read(bFileBuff, 0, 1024) ) != -1 ){
							byte bSize[] = IntToByteArray(nReadBytes);
							System.arraycopy(bSize, 0, bHeader, 2, 4);
							//写入发送缓冲区
							System.arraycopy(bHeader, 0, szSendBuffer, 0, 6);
							System.arraycopy(bFileBuff, 0, szSendBuffer, 6, nReadBytes);
							os.write(szSendBuffer, 0, 6+nReadBytes);
							System.out.println("本次发送出去" + 6+nReadBytes + "字节的数据!");
						}
						System.out.println("文件发送完毕!");
						bFinish = true;
						break;
					}
				}
				default:
					break;
					
				}
			}
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally{
		try{
			if ( server != null )
				server.close();
			if ( client != null )
				client.close();
			if ( is != null )
				is.close();
			if ( os != null )
				os.close();
			if ( fis != null )
				fis.close();
			}
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public static byte[] IntToByteArray(int i) {   
        byte[] result = new byte[4];   
        //由高位到低位
//        result[0] = (byte)((i >> 24) & 0xFF);
//        result[1] = (byte)((i >> 16) & 0xFF);
//        result[2] = (byte)((i >> 8) & 0xFF); 
//        result[3] = (byte)(i & 0xFF);
        result[2] = (byte)((i >> 16) & 0xFF);
        result[3] = (byte)((i >> 24) & 0xFF);
        result[0] = (byte)(i & 0xFF); 
        result[1] = (byte)((i >> 8) & 0xFF);;
        return result;
      }
	 public static int ByteArrayToInt(byte[] bytes) {
         int value= 0;
         //由高位到低位
         for (int i = 0; i < 4; i++) {
             int shift= (4 - 1 - i) * 8;
             value +=(bytes[i] & 0x000000FF) << shift;//往高位游
         }
         return value;
   }

C++代码

#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#pragma comment(lib, "ws2_32")
#include <iostream>
using std::cout;
using std::endl;
#include <process.h>
#include "Define.h"


SOCKET g_skBeat = INVALID_SOCKET;
SOCKET g_skClient = INVALID_SOCKET;
HANDLE g_hBeatThread = NULL;
HANDLE g_hBeatEvent = NULL;




bool WriteRecvBuff(const char* pFileName, byte* lpBuff, UINT nBuffLen, OUT byte** ppRemain, OUT UINT* nRemainLen);

UINT __stdcall HeartbeatThread(void* lpParam)
{
	const char* pBeat = "1";
	int nSendCount = 0, nRet = 0, nRecvCount = 0;
	char szBuffer[10];
	while (true)
	{
		if (SOCKET_ERROR == send(g_skBeat, pBeat, 1, 0))
		{
			nSendCount++;
			cout << "发送心跳包失败" << endl;
			if (nSendCount > 10)
			{
				cout << "发送失败超过10次,心跳线程退出!" << endl;
				break;
			}
			continue;
		}
		nSendCount = 0;
		nRet = recv(g_skBeat, szBuffer, 10, 0);
		if (SOCKET_ERROR == nRet)
		{
			nRecvCount++;
			cout << "接收心跳包失败" << endl;
			if (nRecvCount > 10)
			{
				cout << "接收失败次数超过10次,心跳线程退出!" << endl;
				break;
			}
			continue;
		}
		if (nRet != 1 || szBuffer[0] != '1') 
		{
			cout << "心跳标识不正确,心跳线程退出!" << endl;
			break;
		}
		nRet = WaitForSingleObject(g_hBeatEvent, 3000);
		if (nRet != WAIT_TIMEOUT)
		{
			cout << "事件有信号,心跳线程退出!";
			break;
		}
	}
	
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int k = 0xfff0;
	int* pk = &k;
	WSAData data;
	WSAStartup(MAKEWORD(2, 2), &data);
	SOCKET sk = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	SOCKADDR_IN addr;
	int	nRet, nError;
	const char* pServer = "127.0.0.1";
	const unsigned short nPort = 12345;
	const int nRecvSize = 1024 * 4;
	char szBuffer[100];
	byte* pRecv = (byte*)malloc(nRecvSize), *pRemain = NULL;
	byte* pRemainBuff = (byte*)malloc(nRecvSize * 2);
	UINT	nWriteSize = 0, nRemainLen = 0;
	FileInfo fi;
	if (sk == INVALID_SOCKET)
	{
		cout << "创建套接字失败,系统错误码:" << WSAGetLastError() << endl;
		goto __end;
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons(nPort);
	addr.sin_addr.S_un.S_addr = inet_addr(pServer);
	while (true)
	{
		nRet = WSAConnect(sk, (sockaddr*)&addr, sizeof(SOCKADDR_IN), NULL, NULL, NULL, NULL);
		if (nRet != SOCKET_ERROR)
		{
			cout << "成功连接到服务器:" << pServer << ",端口号:" << nPort << endl;
			break;
		}
		nError = WSAGetLastError();
		Sleep(1000);
	}

	//发送文件请求
	
	byte bflag[2] = { C2S_FILE, C2S_FILE_ASK };
	memcpy(szBuffer, &bflag, 2);
	nRet = send(sk, szBuffer, 2, 0);
	if (SOCKET_ERROR == nRet)
	{
		cout << "发送请求失败!" << endl;
		goto __end;
	}
	bool bFile = false;
	//接收文件
	while (true)
	{
		nRet = recv(sk, (char*)pRecv, nRecvSize, 0);
		if (SOCKET_ERROR == nRet)
			break;
		if (!bFile)
		{
			int nLen = sizeof(FileInfo);
			ZeroMemory(&fi, nLen);
			memcpy(&fi, pRecv + 6, nLen);
			cout << "文件名称:" << fi.szName << "文件大小:" << fi.nSize << endl;
			bflag[1] = C2S_FILE_READY;
			memcpy(szBuffer, &bflag, 2);
			nRet = send(sk, szBuffer, 2, 0);
			if (SOCKET_ERROR == nRet)
			{
				cout << "发送请求失败!" << endl;
				goto __end;
			}
			bFile = true;
		}
		else
		{
			if (nRemainLen > 0)
			{
				memcpy(pRemainBuff, pRemain, nRemainLen);
				memcpy(pRemainBuff + nRemainLen, pRecv, nRet);
				WriteRecvBuff(fi.szName, pRemainBuff, nRet + nRemainLen, &pRemain, &nRemainLen);
			}
			else
				WriteRecvBuff(fi.szName, pRecv, nRet, &pRemain, &nRemainLen);

		}
	}

__end:
	//SetEvent(g_hBeatEvent);
	//WaitForSingleObject(g_hBeatThread, INFINITE);
	//CloseHandle(g_hBeatThread);
	if (sk != INVALID_SOCKET)
		closesocket(sk);
	WSACleanup();
	free(pRemainBuff);
	free(pRecv);
	return 0;
}


bool WriteRecvBuff(const char* pFileName, byte* lpBuff, UINT nBuffLen, OUT byte** ppRemain, OUT UINT* nRemainLen )
{
	UINT nPkgSize = 0, nPos = 0;
	while ( true )
	{
		memcpy(&nPkgSize, lpBuff + 2, 4);
		if (nPos+nPkgSize+6 > nBuffLen)
		{
			*nRemainLen = nBuffLen - nPos;
			*ppRemain = (byte*)malloc(*nRemainLen);
			memcpy(*ppRemain, lpBuff+nPos, *nRemainLen);
			return true;
		}
		FILE* fp = fopen(pFileName, "ab+");
		fwrite(lpBuff + 6 + nPos, 1, nPkgSize, fp);
		fclose(fp);
		nPos += 6 + nPkgSize;
		if (nPos >= nBuffLen)
			break;
	}
	*ppRemain = NULL;
	*nRemainLen = 0;
	return false;
}

有一个地方需要特别注意:Big Endian 和 Little Endian的区别。在Windows上x86架构的CPU一般都是Little Endian,也就是说0xFFF0在内存中,地址从低到高存储的是 F0 FF;在手机上ARM架构的COU一般都是Big Endian,0XFFF0在内存中地址从低到高存储的是 FF F0。因此,两个端在传递数值数据时一定要对这个进行统一处理!

C++程序在Visual Studio上编写,Java程序在Eclipse上编写,同一台电脑上,两端可以同时调试。



 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值