本程序特点:
1.分为client,server,tool 3个包,实现了多个任意客户端之间,进行通信
2.由于使用了包头+包体的信息传递格式,所以可以发送任意长度信息
3.由于没有界面,发送信息时需要指定目标id
一、client包:
public class Client {
private static final String HOST = "127.0.0.1";
private static final int PORT = 9999;
Socket socket ;
Scanner in = new Scanner(System.in);
public void init(){
try {
//登陆
if(login()){
//开启读取服务器端线程
new Thread(new Receive(socket)).start();
//一直读取控制台
while (true){
if(in.hasNextLine()){
//检测消息是否合法
String temp = in.nextLine();
if(temp.contains(":")){
byte [] content = temp.getBytes();
Tool.write(socket, content);
}else {
System.out.println("信息格式不对, 目标id:消息内容");
}
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
private boolean login() throws IOException {
boolean b = false;
System.out.println("请输入用户名密码:");
String username = in.next();
String password = in.next();
socket = new Socket(HOST,PORT);
//登陆检测
Tool.write(socket,(username+" "+password).getBytes());
String flag = new String(Tool.read(socket));
if( flag.equals("true")){
b = true;
}
in.nextLine();
return b;
}
}
客户端接受信息线程
public class Receive implements Runnable {
Socket socket = null;
public Receive(Socket socket){
this.socket = socket;
}
public void run() {
try {
while(true){
String data = new String(Tool.read(socket));
if(data.contains(":")){
System.out.println(data.split(":")[1]);
}else {
System.out.println(data);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
二、server包
public class Server {
private ServerSocket serverSocket;
public void init(){
try {
serverSocket = new ServerSocket(9999);
System.out.println("服务器端--开始监听");
while(true){
Socket socket = serverSocket.accept();
String username = null;
//用户检测
if((username = checkUser(socket)) != null){
ClientHandel hm = new ClientHandel(socket,username);
Thread t = new Thread(hm);
t.start();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 返回用户id
* @param socket
* @return
* @throws IOException
*/
private String checkUser(Socket socket) throws IOException {
String b = null;
byte [] content = Tool.read(socket);
String data [] = new String(content).split(" ");
String username = data[0];
String password = data[1];
b= username;
//这里可以连接数据库进行校验
Tool.write(socket,"true".getBytes());
return b;
}
}
服务器端处理客户端线程
保存所有的socket对象,以用户的id为key
public class ClientHandel implements Runnable {
private String username;
private static HashMap<String, Socket> clientSocket = new HashMap<String, Socket>();
public static int count = 0;
Socket socket = null;
public ClientHandel(Socket socket, String username) {
this.username = username;
count++;
this.socket = socket;
clientSocket.put(username, socket);
System.out.println("用户" + count + "接入");
}
@Override
public void run() {
try {
while (true) {
//读取客户端内容
byte[] data = Tool.read(socket);
//解析目标线程的key
String key = getKey(data);
System.out.println(new String(data));
if (data.length > 1 && key != null) {
//传递给指定线程
Tool.write(clientSocket.get(key), data);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String getKey(byte[] data) throws IOException {
String key = new String(data).split(":")[0];
if (!clientSocket.containsKey(key)) {
Tool.write(socket, "该用户不在线".getBytes());
return null;
}
return key;
}
}
封装了读取,和写入方法,客户端和服务器端都会用到
public class Tool {
public static byte[] read(Socket socket) throws IOException {
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
//包头
byte[] head = new byte[4];
bis.read(head);
byte[] data = new byte[Tool.byteArrayToInt(head)];
//包体
bis.read(data);
return data;
}
public static void write(Socket socket, byte[] content) throws IOException {
//包头,固定4个字节,包含包体长度信息
byte [] head = Tool.intToByteArray1(content.length);
BufferedOutputStream bis = new BufferedOutputStream(socket.getOutputStream());
bis.write(head);
bis.flush();
//包体
bis.write(content);
bis.flush();
}
//int 转字节数组
public static byte[] intToByteArray1(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);
return result;
}
public static byte[] intToByteArray2(int i) throws Exception {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(buf);
out.writeInt(i);
byte[] b = buf.toByteArray();
out.close();
buf.close();
return b;
}
//字节数组转int
public static int byteArrayToInt(byte[] b) {
int intValue=0;
for(int i=0;i<b.length;i++){
intValue +=(b[i] & 0xFF)<<(8*(3-i));
}
return intValue;
}
}
测试-客户端:
public class Main {
public static void main(String args[]){
new Client().init();
}
}
测试-服务器端:
public class Main {
public static void main(String args[]){
new Server().init();
}
}
结果: