即時通訊程序(socket 編程基礎)

這幾天開始學習java的網絡編程,根據這個程序對socket編程進行初步瞭解.


程序效果圖:


源代碼(註釋很詳細):

Message類:

public class Message implements Serializable{
    private String from;//sender
    private String to;  //target
    private String info;//message
    private int type;   //type
    .........
}

MessageType類:

public final class MessageType {
    public static final int TYPE_LOGIN =0x1;
    public  static final  int TYPE_SEND =0x2;
}

Server端:

public class Server {
    public static void main(String[] args) {
        //保存客戶端處理的線程
        Vector<UserThread> vector = new Vector<>();
        ExecutorService es = Executors.newFixedThreadPool(5);
        //創建服務器端的Socket
        try {
            ServerSocket server = new ServerSocket(8888);
            System.out.println("服務器已啓動,等待連接:");
            while (true) {
                Socket socket = server.accept();
                UserThread user = new UserThread(socket, vector);
                es.execute(user);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/*
客戶端處理線程
 */
class UserThread implements Runnable {
    private String name;//客戶端的用戶名稱
    private Socket socket;//服務器連接的客戶端的socket
    Vector<UserThread> vector;//用來傳入服務器的線程池
    private ObjectInputStream ois;//輸入流,接收信息
    private ObjectOutputStream oos;//輸出流,發送信息
    private boolean flag = true;//控制循環的標記

    public UserThread(Socket socket, Vector<UserThread> vector) {//傳入線程要使用的socket和vector
        this.socket = socket;
        this.vector = vector;
        vector.add(this);
    }

    @Override
    public void run() { //開跑
        try {
            System.out.println("客戶端" + socket.getInetAddress().getHostAddress() + "已連接");//先說明連接上的客戶端
            ois = new ObjectInputStream(socket.getInputStream());//裝入客戶端發來的信息流
            oos = new ObjectOutputStream(socket.getOutputStream());//要發出去的信息流
            while (flag) {   //連接後開始持續接收客戶端發來的信息
                //讀取消息對象
                Message msg = (Message) ois.readObject();//讀到的類型要進行強制轉換
                int type = msg.getType();
                switch (type) {
                    case MessageType.TYPE_SEND:
                        String to = msg.getTo();
                        //遍歷vector中的名字,找到線程
                        UserThread ut;
                        int size = vector.size();
                        for (int i = 0; i < size; i++) {
                            ut = vector.get(i);
                            if (to.equals(ut.name) && ut != this) {
                                ut.oos.writeObject(msg);
                                break;
                            }
                        }
                        break;
                    case MessageType.TYPE_LOGIN:  //客戶端一開始連接時發來的信息會是login類型,爲了提高效率,這個情況放後面
                        name = msg.getFrom();     //獲取客戶端姓名(唯一)
                        msg.setInfo("歡迎你");    //同樣使用msg,把info設置爲歡迎你
                        oos.writeObject(msg);     //接收到login信息後發送歡迎你信息給客戶端
                        break;
                    default:
                        break;
                }
            }
            ois.close();
            oos.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
client端:

public class Client {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        ExecutorService es = Executors.newSingleThreadExecutor();//單線程池
        try {
            Socket socket = new Socket("localhost", 8888);
            System.out.println("服務器連接成功");
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            //先向服務器發送消息,然後在接收服務器的信息,纔會收到"歡迎你"
            System.out.println("請輸入你的名稱");
            String name = input.nextLine();
            Message msg = new Message(name, null, null, MessageType.TYPE_LOGIN);
            objectOutputStream.writeObject(msg);
            msg = ((Message) objectInputStream.readObject());
            System.out.println(msg.getInfo() + msg.getFrom());
            //啓動讀取消息的線程
            ReadInfoThread read = new ReadInfoThread(objectInputStream);
            es.execute(read);
            //使用主線程實現發送消息
            boolean flag = true;
            while (flag) {  //一直可以發消息,且這時候的type都是TYPE_SEND
                msg = new Message();
                System.out.println("To:");
                msg.setTo(input.nextLine());
                msg.setType(MessageType.TYPE_SEND);
                msg.setFrom(name);
                System.out.println("info:");
                msg.setInfo(input.nextLine());
                objectOutputStream.writeObject(msg);
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

//讀取消息線程
class ReadInfoThread implements Runnable {

    private ObjectInputStream in;  //服務器發來的輸入流
    private boolean flag = true;   //是否讀的標誌

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public ReadInfoThread(ObjectInputStream in) {
        this.in = in;
    }

    @Override
    public void run() {

        try {
            while (flag) {   //一直讀
                Message msg = ((Message) in.readObject());   //強制類型轉換
                //除了登錄的時候,服務器把info設爲"歡迎你",後面的msg info都是客戶端自己構造的
                System.out.println("【" + msg.getFrom() + "】對我說" + "\"" + msg.getInfo() + "\"");
            }
            if (in != null) {
                in.close();
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

發佈了35 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章