socket以流的形式传输不间断的数据,所以呢当传输多个数据包是需要完整的读取消息包内的数据。
为了完整的读取消息,这里我写了个简单的demo,数据包协议为
内容长度+内容。
直接上代码
服务端:
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class SocketStreamServer {
private boolean running = false;
private ServerSocket serverSocket;
private OnMessageListener messageListener;
private AtomicInteger mConnections = new AtomicInteger(0);
private static final Executor sThreadPools = Executors.newCachedThreadPool();
public SocketStreamServer(int port, OnMessageListener messageListener) throws IOException {
serverSocket = new ServerSocket(port);
running = true;
this.messageListener = messageListener;
}
public void start() {
sThreadPools.execute(new Runnable() {
@Override
public void run() {
try {
while (true) {
Socket socket = serverSocket.accept();
runSocket(socket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public int getActiveConnetions() {
return mConnections.get();
}
private void runSocket(final Socket socket) {
sThreadPools.execute(new Runnable() {
@Override
public void run() {
mConnections.incrementAndGet();
handleReaderServer(socket);
}
});
}
private void handleReaderServer(final Socket socket) {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] buff = new byte[8];
int msgTotalLen = 0; //当前包消息总长度
int currMsgLen = 0; //已读消息长度
byte[] remaining = null; //上次读取完成后下个包部分
ByteBuffer msgBuffer = null; //已读消息部分
while (true) {
int len = inputStream.read(buff);
if (msgTotalLen == 0) {
//新的包开始
byte[] rF = null;
int rlen = 0;
if (remaining != null) {
//有上次没有读完的部分,合并一起读
rlen = remaining.length + len;
rF = new byte[rlen];
//合并插入最前面
System.arraycopy(remaining, 0, rF, 0, remaining.length);
System.arraycopy(buff, 0, rF, remaining.length, len);
remaining = null;
} else {
rF = buff;
rlen = len;
}
msgTotalLen = readMsgHeaderLength(rF, 0);
if (msgTotalLen + 4 < rlen) {
//用于处理一次读取到的流里面包含多个包的情况
boolean canFullRead = true;
int lastLen = 0;
while (canFullRead) {
msgBuffer = ByteBuffer.allocate(msgTotalLen);
msgBuffer.put(Arrays.copyOfRange(rF, 4 + lastLen, msgTotalLen + 4 + lastLen));
onRecvMsg(msgBuffer);
lastLen += msgTotalLen + 4;
if (lastLen + 4 < rlen) {
msgTotalLen = readMsgHeaderLength(rF, lastLen);
if (lastLen + msgTotalLen + 4 < rlen) {
canFullRead = true;
} else {
canFullRead = false;
remaining = Arrays.copyOfRange(rF, lastLen, rlen);
}
} else {
canFullRead = false;
remaining = Arrays.copyOfRange(rF, lastLen, rlen);
}
}
msgTotalLen = 0;
currMsgLen = 0;
} else {
msgBuffer = ByteBuffer.allocate(msgTotalLen);
msgBuffer.put(Arrays.copyOfRange(rF, 4, rlen));
currMsgLen += (rlen - 4);
}
} else {
if (currMsgLen + len >= msgTotalLen) {
//当前包要读完了
int n = msgTotalLen - currMsgLen; //计算当前数据包分割点
msgBuffer.put(buff, 0, n);
onRecvMsg(msgBuffer);
msgTotalLen = 0;
currMsgLen = 0;
if (n < len) {
//保存下次继续读
remaining = Arrays.copyOfRange(buff, n, len);
} else {
remaining = null;
}
} else {
//没有读完,继续
currMsgLen += len;
msgBuffer.put(buff);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
mConnections.decrementAndGet();
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
protected void onRecvMsg(ByteBuffer byteBuffer) {
byteBuffer.flip();
if (messageListener != null) {
messageListener.onRecv(byteBuffer.array());
}
}
private int readMsgHeaderLength(byte[] array, int startPosition) {
return Utils.byteArrayToInt(Arrays.copyOfRange(array, startPosition, startPosition + 4));
}
public void stopServer() {
running = false;
}
public interface OnMessageListener {
void onRecv(byte[] bytes);
}
}
客户端:
import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.*;
public class MySocketServer {
private static final int PORT=44560;
public static void main(String[] args) {
try {
new SocketStreamServer(PORT, new SocketStreamServer.OnMessageListener() {
@Override
public void onRecv(byte[] bytes) {
System.err.println("onRecv --> "+new String(bytes));
}
}).start();
startClient();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void startClient() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//数据包格式为 内容长度(int)+内容
Socket socket = new Socket("127.0.0.1", PORT);
final OutputStream stream = socket.getOutputStream();
Random random = new Random();
Scanner scanner=new Scanner(System.in);
while (true) {
// String s = getString(random.nextInt(10) + 1);
// System.out.println("client out: " + s);
System.out.println("input:");
String s= scanner.nextLine();
stream.write(encodeData(s.getBytes())); //发包
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public static byte[] encodeData(byte[] msgData) {
int len = msgData.length;
return Utils.mergeArray(Utils.intToByteArray(len),msgData);
}
}
public class Utils {
public static byte[] intToByteArray(int value) {
return new byte[]{
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
(byte) value};
}
public static int byteArrayToInt(byte[] array) {
return array[0] << 24 | (array[1] & 0xFF) << 16 | (array[2] & 0xFF) << 8 | (array[3] & 0xFF);
}
public static byte[] mergeArray(byte[]... bytes){
int count=0;
for (byte[] arr:bytes){
count+=arr.length;
}
byte[] newBytes=new byte[count];
int destPos=0;
for (byte[] arr:bytes){
System.arraycopy(arr,0,newBytes,destPos,arr.length);
destPos+=arr.length;
}
return newBytes;
}
}
运行之后,每次发送的包都会在头部4位表示里面消息长度。