近期在做一个视频采集传输的项目,使用到了VC++与Java的Socket数据传输,将其要点记录下来,以备查询。
1. VC端
int err;
WORD versionRequired;
WSADATA wsaData;
versionRequired=MAKEWORD(1,1);
err=WSAStartup(versionRequired,&wsaData);//协议库的版本信息
if (!err) {
printf("客户端嵌套字已经打开!\n");
}else{
printf("ERROR:客户端的嵌套字打开失败!\n");
AfxMessageBox("ERROR:客户端的嵌套字打开失败!\n");
return 1;//结束
}
SendPicInfo m_sendPicInfo;
//这里的缓冲区大小需要根据抓图的分辨率大小自己调节,建议设置成2*图片的分辨率宽*图片的分辨率高
char * Jpeg = new char[352*288*2];
DWORD len = 352*288*2;
DWORD SizeReturned = 0;
SSOCXInfo* pInfo = (SSOCXInfo*) lpParam;
// Jpeg结构参数
NET_DVR_JPEGPARA struJpegPara;
memset(&struJpegPara, 0, sizeof(NET_DVR_JPEGPARA));
struJpegPara.wPicQuality = 0;
//struJpegPara.wPicSize = (WORD) 0;
struJpegPara.wPicSize = 0xff;
SOCKADDR_IN clientsock_in;
clientsock_in.sin_addr.S_un.S_addr=inet_addr("192.168.1.160");
clientsock_in.sin_family=AF_INET;
clientsock_in.sin_port=htons(8765);
// 调用海康威视接口,截取摄像头图像
if (NET_DVR_CaptureJPEGPicture_NEW(i,1,&struJpegPara, Jpeg, len, &SizeReturned))
{
// 保存为文件
//FILE *file=NULL;
//file = fopen("d:/test/库文件/11.jpg" ,"wb");
//fwrite(Jpeg, SizeReturned, 1, file);
//fclose(file);
//file=NULL;
//char * imgLen = new char[sizeof(DWORD)];
memcpy(&m_sendPicInfo.dataLength, &SizeReturned,sizeof(DWORD));
memcpy(&m_sendPicInfo.cmrIp, pInfo->saveFile, strlen(pInfo->saveFile));
// 发送到服务器
SOCKET clientSocket=socket(AF_INET,SOCK_STREAM,0);
// 建立连接
connect(clientSocket,(SOCKADDR*) &clientsock_in, sizeof(SOCKADDR));
// 发送IP及图形数据长度到服务器
send(clientSocket, (char *)&m_sendPicInfo, sizeof(SendPicInfo), 0);
Sleep(50);
// 发送获取到的JPG字节流
send(clientSocket, Jpeg, SizeReturned, 0);
Sleep(250);
//测试发送中文字符
//CString sendStr = "[hello,this is client测试]";
//send(clientSocket, sendStr, strlen(sendStr),0);c (char *)&sendUser, sizeof(UsrData)
// 接收服务器端的反馈信息
//char receiveBuf[100];
//recv(clientSocket, receiveBuf,101,0);
//printf("Recv:%s\n",receiveBuf);
closesocket(clientSocket);
WSACleanup ();
}
结构体
struct GetPicInfo
{
int iDeviceIndex;
LONG lLoginID;
CString chDeviceName; //device name
CString chDeviceIP;
//CString saveFile;
HANDLE hMutex;
HANDLE hThread;
DWORD ThreadID;
SSOCXInfo chkInfo;
};
struct SendPicInfo
{
char dataLength [4];
char cmrIp[16];
......
};
- JAVA端(服务端)
public void doThing1() {
try {
ServerSocket server = new ServerSocket(PORT);
Socket client = null;
// 通过调用Executors类的静态方法,创建一个ExecutorService实例
// ExecutorService接口是Executor接口的子接口
Executor service = Executors.newCachedThreadPool();
while (true) {
// 等待客户端的连接
client = server.accept();
//System.out.println("与客户端连接成功!");
// 调用execute()方法时,如果必要,会创建一个新的线程来处理任务,但它首先会尝试使用已有的线程,
// 如果一个线程空闲60秒以上,则将其移除线程池;
// 另外,任务是在Executor的内部排队,而不是在网络中排队
service.execute(new ServerThread(client));
}
server.close();
} catch (IOException ee) {
ee.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
接收线程
package cn.xaele.thread;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.Socket;
import javax.imageio.ImageIO;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import cn.xaele.utils.ByteUtil;
public class ServerThread implements Runnable {
// 日志输出
private static final Logger log = LogManager.getLogger(ServerThread.class);
private Socket client = null;
private static boolean isTest = false;
public ServerThread(Socket client) {
this.client = client;
}
// 处理通信细节的静态方法,这里主要是方便线程池服务器的调用
public static void execute(Socket client) {
int tmpNum = (int) (Math.random() * 1000);
if (isTest) {
log.info(tmpNum + "----------->进入线程");
}
try {
long startTime = System.currentTimeMillis();
// 获取Socket的输出流,用来向客户端发送数据
DataInputStream dis = new DataInputStream(client.getInputStream());
int len = 0;
byte[] buffer = new byte[1024];
len = dis.read(buffer, 0, 20);
if (isTest) {
System.out.println("读取字节长度[" + len + "]");
}
byte[] dataLength = new byte[4];
System.arraycopy(buffer, 0, dataLength, 0, 4);
long picLength = ByteUtil.bytesToLong(dataLength);
if (isTest) {
System.out.println("图形字节长度[" + picLength + "]");
}
byte[] cmrIp = new byte[16];
System.arraycopy(buffer, 4, cmrIp, 0, 16);
// 对VC上传的字节进行转码,防止中文乱码
String cmpIpStr = new String(cmrIp, "GB2312").trim();
if (isTest) {
System.out.println("[" + cmpIpStr + "]");
}
// 读取VC上传的jpg图形字节流
buffer = new byte[(int) picLength];
len = dis.read(buffer, 0, (int) picLength);
if (isTest) {
System.out.println("读取长度[" + buffer.length + "]");
}
ByteArrayInputStream in = new ByteArrayInputStream(buffer); // 将buffer作为输入流;
BufferedImage image = ImageIO.read(in); // 将in作为输入流,读取图片存入image中,而这里in可以为ByteArrayInputStream();
ImageIO.write((BufferedImage) image, "JPEG", new File("d:/" + cmpIpStr + ".jpg"));
in.close();
if (isTest) {
log.info("耗时:[" + (System.currentTimeMillis() - startTime) + "]ms");
}
dis.close();
client.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
log.info(tmpNum + "--------->退出线程");
}
}
@Override
public void run() {
execute(client);
}
}
3.注意事项
3.1 在Java中对需要进行socket传递的内容按照C++格式进行转码。常用代码如下:
public static int bytesToInt(byte[] b) {
int s = 0;
for (int i = 3; i > -1; i--) {
s *= 256;
s += b[i] < 0 ? b[i] + 256 : b[i];
}
return s;
}
public static int bytesToInt(char[] b) {
int s = 0;
for (int i = 3; i > -1; i--) {
s *= 256;
s += b[i] < 0 ? b[i] + 256 : b[i];
}
return s;
}
public static byte[] intToBytes(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 static char[] intToChars(int n) {
char[] b = new char[4];
b[0] = (char) (n & 0xff);
b[1] = (char) (n >> 8 & 0xff);
b[2] = (char) (n >> 16 & 0xff);
b[3] = (char) (n >> 24 & 0xff);
return b;
}
public static long bytesToLong(byte[] b) {
int s = 0;
for (int i = 3; i > -1; i--) {
s *= 256;
s += b[i] < 0 ? b[i] + 256 : b[i];
}
return s;
}
public static long bytesToLong(char[] b) {
int s = 0;
for (int i = 3; i > -1; i--) {
s *= 256;
s += b[i] < 0 ? b[i] + 256 : b[i];
}
return s;
}
public static byte[] longToBytes(long 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 static float bytesToFloat(byte[] b) {
int num = bytesToInt(b);
return Float.intBitsToFloat(num);
}
public static float bytesToFloat(char[] b) {
int num = bytesToInt(b);
return Float.intBitsToFloat(num);
}
public static byte[] floatToBytes(float f) {
return intToBytes(Float.floatToRawIntBits(f));
}
public static double bytesToDouble(byte[] b) {
long num = bytesToLong(b);
return Double.longBitsToDouble(num);
}
public static byte[] doubleToBytes(double f) {
return longToBytes(Double.doubleToRawLongBits(f));
}
public static String bytesToString(byte[] b, int length) {
return new String(b, 0, length);
}
public static byte[] stringToBytes(String s, int length) {
while (s.getBytes().length < length)
s += "\0";
return s.getBytes();
}
public static String toHexString(int num) {
String tmp = (java.lang.Integer.toHexString((char) num & 0XFF));
return (tmp.length() == 1) ? "0" + tmp : tmp;
}
/**
* java字节码转字符串
*
* @param b
* @return
*/
public static String printByte(char[] b) { // 一个字节的数,// 转成16进制字符串
String hs = "";
String tmp = "";
for (int n = 0; n < b.length; n++) {
// 整数转成十六进制表示
tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (tmp.length() == 1) {
hs = hs + "0" + tmp + " ";
} else {
hs = hs + tmp + " ";
}
}
tmp = null;
return hs.toUpperCase(); // 转成大写
}
/**
* java字节码转字符串
*
* @param b
* @return
*/
public static String printByte(byte[] b) { // 一个字节的数,// 转成16进制字符串
String hs = "";
String tmp = "";
for (int n = 0; n < b.length; n++) {
// 整数转成十六进制表示
tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (tmp.length() == 1) {
hs = hs + "0" + tmp + " ";
} else {
hs = hs + tmp + " ";
}
}
tmp = null;
return hs.toUpperCase(); // 转成大写
}
/**
* java字节码转字符串
*
* @param b
* @return
*/
public static String printByte(int[] b) { // 一个字节的数,// 转成16进制字符串
String hs = "";
String tmp = "";
for (int n = 0; n < b.length; n++) {
// 整数转成十六进制表示
tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (tmp.length() == 1) {
hs = hs + "0" + tmp + " ";
} else {
hs = hs + tmp + " ";
}
}
tmp = null;
return hs.toUpperCase(); // 转成大写
}
/**
* int转byte
*
* @param x
* @return
*/
public static byte intToByte(int x) {
return (byte) x;
}
/**
* byte转int
*
* @param b
* @return
*/
public static int byteToInt(byte b) {
// Java的byte是有符号,通过 &0xFF转为无符号
return b & 0xFF;
}
/**
* byte[]转int
*
* @param b
* @return
*/
public static int byteArrayToInt(byte[] b) {
return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16 | (b[0] & 0xFF) << 24;
}
public static int byteArrayToInt(byte[] b, int index) {
return b[index + 3] & 0xFF | (b[index + 2] & 0xFF) << 8 | (b[index + 1] & 0xFF) << 16
| (b[index + 0] & 0xFF) << 24;
}
/**
* int转byte[]
*
* @param a
* @return
*/
public static byte[] intToByteArray(int a) {
return new byte[] { (byte) ((a >> 24) & 0xFF), (byte) ((a >> 16) & 0xFF), (byte) ((a >> 8) & 0xFF),
(byte) (a & 0xFF) };
}
/**
* short转byte[]
*
* @param b
* @param s
* @param index
*/
public static void byteArrToShort(byte b[], short s, int index) {
b[index + 1] = (byte) (s >> 8);
b[index + 0] = (byte) (s >> 0);
}
/**
* byte[]转short
*
* @param b
* @param index
* @return
*/
public static short byteArrToShort(byte[] b, int index) {
return (short) (((b[index + 0] << 8) | b[index + 1] & 0xff));
}
/**
* 16位short转byte[]
*
* @param s
* short
* @return byte[]
*/
public static byte[] shortToByteArr(short s) {
byte[] targets = new byte[2];
for (int i = 0; i < 2; i++) {
int offset = (targets.length - 1 - i) * 8;
targets[i] = (byte) ((s >>> offset) & 0xff);
}
return targets;
}
/**
* byte[]转16位short
*
* @param b
* @return
*/
public static short byteArrToShort(byte[] b) {
return byteArrToShort(b, 0);
}
/**
* long转byte[]
*
* @param x
* @return
*/
public static byte[] longToBytes1(long x) {
buffer.putLong(0, x);
return buffer.array();
}
/**
* byte[]转Long
*
* @param bytes
* @return
*/
public static long bytesToLong1(byte[] bytes) {
buffer.put(bytes, 0, bytes.length);
buffer.flip();// need flip
return buffer.getLong();
}
/**
* 从byte[]中抽取新的byte[]
*
* @param data
* - 元数据
* @param start
* - 开始位置
* @param end
* - 结束位置
* @return 新byte[]
*/
public static byte[] getByteArr(byte[] data, int start, int end) {
byte[] ret = new byte[end - start];
for (int i = 0; (start + i) < end; i++) {
ret[i] = data[start + i];
}
return ret;
}
/**
* 流转换为byte[]
*
* @param inStream
* @return
*/
public static byte[] readInputStream(InputStream inStream) {
ByteArrayOutputStream outStream = null;
try {
outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
byte[] data = null;
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
data = outStream.toByteArray();
return data;
} catch (IOException e) {
return null;
} finally {
try {
if (outStream != null) {
outStream.close();
}
if (inStream != null) {
inStream.close();
}
} catch (IOException e) {
return null;
}
}
}
/**
* byte[]转inputstream
*
* @param b
* @return
*/
public static InputStream readByteArr(byte[] b) {
return new ByteArrayInputStream(b);
}
/**
* byte数组内数字是否相同
*
* @param s1
* @param s2
* @return
*/
public static boolean isEq(byte[] s1, byte[] s2) {
int slen = s1.length;
if (slen == s2.length) {
for (int index = 0; index < slen; index++) {
if (s1[index] != s2[index]) {
return false;
}
}
return true;
}
return false;
}
/**
* byte数组转换为Stirng
*
* @param s1
* -数组
* @param encode
* -字符集
* @param err
* -转换错误时返回该文字
* @return
*/
public static String getString(byte[] s1, String encode, String err) {
try {
return new String(s1, encode);
} catch (UnsupportedEncodingException e) {
return err == null ? null : err;
}
}
/**
* byte数组转换为Stirng
*
* @param s1-数组
* @param encode-字符集
* @return
*/
public static String getString(byte[] s1, String encode) {
return getString(s1, encode, null);
}
/**
* 字节数组转16进制字符串
*
* @param b
* @return
*/
public static String byteArrToHexString(byte[] b) {
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
/**
* 16进制字符创转int
*
* @param hexString
* @return
*/
public static int hexStringToInt(String hexString) {
return Integer.parseInt(hexString, 16);
}
/**
* 十进制转二进制
*
* @param i
* @return
*/
public static String intToBinary(int i) {
return Integer.toBinaryString(i);
}
3.2 对于大数据量传输,需要分两次,第一次传送基本信息,并在其中指定待传数据长度(及其传递次数,如果有必要的话);第二次传送大数据信息(或按照第一次的约定多次传输),在接收端进行组装。
3.3 如果传递的字节长度较大,且需多次传输,建议使用ByteArrayOutputStream bo = new ByteArrayOutputStream(1024);