一个简单的Socket聊天程序, 当然是多客户的(客户代码差不多一样),可以添加一些东西,但大概主体是这样的<...>
难点:1.客户端什么时候去读取信息.
2. 服务器应该怎么存储客户端的每个连接.
对于第一个问题:
有这样三个回答:
1.每隔一段时间查看服务器一次
优点: 可行
缺点: 有点复杂,这样以来服务器就必须知道客户端哪些已经读取,而哪些内容还没有读取,所以服务器就需要存储信息;
再者,间隔时间也不好取,太大,延迟高,太小,效率太低.
2.在用户发送消息时,读取
有点: 简单
缺点: 如果有多个客户,这个客户又有点懒,没发送,这样他就接受不到其他用户发送的消息了.
3.在消息被发送到服务器时,读取
优点: 效率高
缺点: 这样必须做一个循环单独来接收消息,但是要放哪里呢(为了防止影响用户与GUI的交互,我们创建一个线程专门监听), GUI启动后,
除非有事件被触发,否则没有一处是在运转的
第二个问题嘛,我们存储每个客户和服务器连接后的流,因为客户个数未知,故采用自动增长的线性表即可.
下面是代码实现.
1 1 package jffx.blogs.net; 2 2 3 3 import java.io.*; 4 4 import java.net.ServerSocket; 5 5 import java.net.Socket; 6 6 import java.util.ArrayList; 7 7 import java.util.Date; 8 8 9 9 //服务器并没有用GUI 10 10 //当然也可以,不过没有必要, 11 11 //一般是写入服务器的配置文件. 12 12 //当然,一切看你 13 13 public class Exercise2_Server { 14 14 /** 15 15 * 保存和服务器连接的每个客户端流 16 16 */ 17 17 ArrayList<DataOutputStream> clientOutputStream = null ; 18 18 19 19 20 20 public static void main(String[] args) { 21 21 new Exercise2_Server().start() ; 22 22 } 23 23 24 24 /** 25 25 * 程序主要处理阶段 26 26 */ 27 27 private void start() { 28 28 //为存储客户流分配空间 29 29 clientOutputStream = new ArrayList<>() ; 30 30 31 31 try { 32 32 ServerSocket server = new ServerSocket(4242) ; 33 33 34 34 //使服务器一直处于监听状态 35 35 while(true) { 36 36 Socket sock = server.accept() ; 37 37 DataOutputStream out = new DataOutputStream( 38 38 new BufferedOutputStream(sock.getOutputStream()) 39 39 ) ; 40 40 clientOutputStream.add(out) ; 41 41 42 42 //处理每个客户 43 43 new Thread(new ClientHandler(sock)).start() ; 44 44 //显示消息 45 45 System.out.println(new Date()) ; 46 46 System.out.println(" 建立一个连接") ; 47 47 } 48 48 } catch (Exception ex) { 49 49 ex.printStackTrace() ; 50 50 } 51 51 } 52 52 53 53 /** 54 54 * 单独处理每个客户的任务 55 55 */ 56 56 private class ClientHandler implements Runnable { 57 57 Socket socket = null ; 58 58 DataInputStream read = null ; 59 59 public ClientHandler(Socket socket) { 60 60 try { 61 61 this.socket = socket ; 62 62 read = new DataInputStream( 63 63 new BufferedInputStream(socket.getInputStream()) 64 64 ) ; 65 65 } catch (Exception ex) { 66 66 ex.printStackTrace() ; 67 67 } 68 68 } 69 69 @Override 70 70 public void run() { 71 71 String msg ; 72 72 try { 73 73 //从客户端读取所有消息 74 74 while((msg = read.readUTF()) != null) { 75 75 System.out.println("read : " + msg) ; 76 76 sendToEveryOne(msg) ; 77 77 } 78 78 } catch (Exception ex) { 79 79 ex.printStackTrace() ; 80 80 } 81 81 } 82 82 } 83 83 //发送消息给所有客户 84 84 private void sendToEveryOne(String msg) { 85 85 try { 86 86 for (DataOutputStream writer : clientOutputStream) { 87 87 writer.writeUTF(msg); 88 88 writer.flush() ; //立刻发送输出流 89 89 } 90 90 } catch (Exception ex) { 91 91 ex.printStackTrace() ; 92 92 } 93 93 } 94 94 95 95 }
1 package jffx.blogs.net; 2 3 import javafx.application.Application; 4 import javafx.scene.Scene; 5 import javafx.scene.control.Button; 6 import javafx.scene.control.ScrollPane; 7 import javafx.scene.control.TextArea; 8 import javafx.scene.control.TextField; 9 import javafx.scene.layout.BorderPane; 10 import javafx.scene.layout.HBox; 11 import javafx.stage.Stage; 12 13 import java.io.BufferedInputStream; 14 import java.io.BufferedOutputStream; 15 import java.io.DataInputStream; 16 import java.io.DataOutputStream; 17 import java.net.Socket; 18 import java.util.Date; 19 20 /** 21 * 聊天程序的客户端 22 */ 23 public class Exercise2_Client1 extends Application 24 { 25 TextArea ta = new TextArea() ; 26 TextField tf = new TextField() ; 27 DataInputStream in = null ; 28 DataOutputStream out = null ; 29 Socket socket ; 30 31 @Override 32 public void start(Stage primaryStage) { 33 //画图 34 BorderPane mainPane = new BorderPane() ; 35 36 mainPane.setCenter(new ScrollPane(ta)) ; 37 ta.setPrefColumnCount(60); 38 ta.setPrefRowCount(80); 39 ta.setEditable(false) ; 40 41 Button bt = new Button("Send") ; 42 HBox hbox = new HBox(10) ; 43 hbox.getChildren().addAll(tf, bt) ; 44 tf.setPrefColumnCount(50); 45 mainPane.setBottom(hbox); 46 47 //初始化流 48 setUpNetWorking(); 49 50 //设置GUI事件 -- lambda() 51 bt.setOnAction(event -> { 52 try { 53 out.writeUTF(tf.getText()) ; 54 out.flush() ; 55 } catch (Exception ex) { 56 ex.printStackTrace() ; 57 } 58 //清空文本域 59 tf.setText(""); 60 }) ; 61 62 //创建线程单独处理接收消息 63 //和用户与GUI交互分开 64 Thread readThread = new Thread(() -> { 65 String message ; 66 //反复读取服务器发送的内容 67 try { 68 while((message = in.readUTF()) != null) { 69 System.out.println("read : " + ta.getText()) ; 70 ta.appendText(new Date() + " "); 71 ta.appendText(" " + message + " ") ; 72 } 73 } catch (Exception ex) { 74 ex.printStackTrace() ; 75 } 76 }); 77 readThread.start() ; 78 79 //显示 80 Scene scene = new Scene(mainPane, 400, 600) ; 81 primaryStage.setScene(scene) ; 82 primaryStage.setTitle("Client") ; 83 primaryStage.show() ; 84 } 85 //初始化流 86 private void setUpNetWorking() { 87 try { 88 socket = new Socket("127.0.0.1", 4242) ; 89 in = new DataInputStream(new BufferedInputStream(socket.getInputStream())) ; 90 out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())) ; 91 System.out.println("时间:" + new Date()) ; 92 System.out.println(" 连接建立!") ; 93 } catch (Exception ex) { 94 ex.printStackTrace() ; 95 } 96 } 97 98 }