简单模拟qq聊天程序(TCP版)

来源:互联网 发布:java获取上下文路径 编辑:程序博客网 时间:2024/05/01 15:28

本程序特点:

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


三、tool包

封装了读取,和写入方法,客户端和服务器端都会用到

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

结果:







1 0