要实现:多个客户端,要使用多线程
image.png
image.png
image.png

(每来一个管道就交给线程,每一个线程都会有自己专属的管道)

image.png

  1. package com.itheima.d6_socket2;
  2. import java.io.OutputStream;
  3. import java.io.PrintStream;
  4. import java.net.Socket;
  5. import java.util.Scanner;
  6. /**
  7. * 实现服务端可以同时处理多个客户端的消息
  8. */
  9. public class ClientDemo1 {
  10. public static void main(String[] args) {
  11. try {
  12. // 1. 创建Socket通信管道请求有服务端的连接
  13. // public Socket(String host, int port) 调用其有参构造器,
  14. // host是服务端的ip地址,port是服务端的地址(要和服务端接通管道)
  15. Socket socket = new Socket("127.0.0.1",7777); // 这里用本机当作服务端
  16. // 2. 从socket通信管道中得到一个字节输出流(写数据,用最高的打印流),负责发送数据
  17. OutputStream os = socket.getOutputStream(); // os是一个输出流对象
  18. // 3. 把低级的字节流包装成打印流
  19. PrintStream ps = new PrintStream(os); // 包装成打印流,打回给os,os是socket(连接的管道的对象)衔接的管道 输出流 // 然后
  20. // 通过socket发送给服务端
  21. Scanner sc = new Scanner(System.in);
  22. while (true) { // 实现多发多收,服务端和客户端都要定义while
  23. System.out.println("请说:");
  24. String msg = sc.nextLine();
  25. // 4. 发送消息
  26. ps.println(msg); // 写数据 要刷新
  27. ps.flush();
  28. }
  29. // 关闭资源(不建议直接关闭资源,TCP可以一直建立连接, 可以让用户离线自动关闭)
  30. // socket.close();
  31. } catch ( Exception e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }
  1. package com.itheima.d6_socket2;
  2. import java.io.BufferedReader;
  3. import java.io.InputStream;
  4. import java.io.InputStreamReader;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7. /**
  8. * 目标:开发Socket网络编程入门代码的服务端,实现接收消息
  9. */
  10. public class ServiceDemo2 {
  11. public static void main(String[] args) {
  12. try {
  13. System.out.println("服务端启动成功:");
  14. // 1.注册端口 (注意端口号要和客户端写的端口号要一致,这样客户端才能找到服务端)
  15. ServerSocket serverSocket = new ServerSocket(7777);
  16. // a. 定义一个死循环由主线程负责不断的接收客户端的Socket管道连接
  17. while (true){
  18. // 2. 每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
  19. Socket socket = serverSocket.accept();
  20. // getRemoteSocketAddress 得到远程地址(这个是客户端地址)
  21. System.out.println(socket.getRemoteSocketAddress() + "上线了");
  22. // 3. 开始创建独立线程处理socket (每来一个管道就交给线程,每一个线程都会有自己专属的管道)
  23. new ServerReaderThread(socket).start(); // 创建线程对象,并启动
  24. }
  25. } catch ( Exception e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }
package com.itheima.d6_socket2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;  // 创建Socket管道对象
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {    // 子线程都在这里跑
        try {
            // 3. 从socket通信管道中得到一个字节输入流
            InputStream is = socket.getInputStream(); // 读数据用输入流
            // 4. 把字节输入流包装成缓冲字符输入流进行消息的接收
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            // 5. 按照行读取消息
            String msg;
            while ((msg = br.readLine()) != null){  // 如果每收到数据,就会出异常,到catch
                System.out.println(socket.getRemoteSocketAddress() + "说了: " + msg);
            }
        } catch ( Exception e) {  // 异常了,就执行这里,表示下线了
            System.out.println(socket.getRemoteSocketAddress() + "下线了");
        }
    }
}