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

本程序特点:

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

结果:







评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值