這幾天開始學習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(); } } }