Java实现TCP一服务端对多客户端Socket通信网络编程

TCP协议特点:

  • 面向连接,只能由客户端主动发送数据给服务端,服务端接收到数据后可以给客户端响应
  • 通过三次握手建立连接,四次挥手断开连接
  • 基于IO流进行数据传输,传输大小没有限制
  • 速度慢,传输可靠

使用场景

  • 文件上传下载
  • 邮件发送接收
  • 远程登陆
    TCP通信也叫Socket网络编程

Socket
一个该类对象代表一个客户端程序

  • 构造器
    public Socket(String host,int port)
    使用该方法会立即连接指定服务器程序,如果连接不成功会抛出异常,连接成功表示三次握手通过
  • 方法
    OutputStream getOutputStream() 获得字节输出流对象
    InputStream getInputStream() 获得字节输入流对象
  • 流程
    1 客户端要求请求与服务器的socket管道连接
    2 从socket通信管道获取字节输出流
    3 通过字节输出流写出数据

ServerSocket
一个该类对象代表一个服务端程序

  • 构造器
    public ServerSocket(int port)
  • 方法
    public Socket accept() 等待接收一个客户端的socket管道请求,连接成功返回一个Socket对象
  • 流程
    1 注册端口,接收客户端的socket管道连接
    2 从socket通信管道获取字节输入流
    3 通过字节输入流读取数据

客户端

class TCPClient{
    public static void main(String[] args) throws IOException {
        System.out.println("客户端启动。。。");
        //连接本机端口号7777的程序
        Socket socket=new Socket("127.0.0.1",7777);
        //获取字节输出流
        OutputStream os = socket.getOutputStream();
        //将字节输出流包装成打印流
        PrintStream ps = new PrintStream(os);
        //循环发送消息
        while (true){
            Scanner scanner = new Scanner(System.in);
            System.out.print("请发消息: ");
            //打印输入的消息
            ps.println(scanner.nextLine());
        }
    }
}

服务端

class TCPServer{
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动。。。");
        //指定端口号为7777,要和客户端请求的端口号一致
        ServerSocket serverSocket = new ServerSocket(7777);
        //循环接收消息
        while (true){
        	//获取一个客户端连接
            Socket socket = serverSocket.accept();
            //启动一个新的线程去处理
            new Server(socket).start();
        }
    }
}

处理客户端的线程任务

class Server extends Thread{
	//封装客户端socket
    private Socket socket;
    
    public Server(Socket socket){
   		 this.socket=socket;
    }

    @Override
    public void run() {
        try{
            //获取字节输入流
            InputStream is = socket.getInputStream();
            //将字节输入流转为字符输入流
            InputStreamReader reader = new InputStreamReader(is);
            //将字符输入流包装成缓冲字符输入流
            BufferedReader br = new BufferedReader(reader);
            //循环读取消息
            String msg;
            while ((msg=br.readLine())!=null){
                System.out.println(socket.getRemoteSocketAddress()+"say: "+msg);
            }
        }catch (Exception e){
            System.out.println(socket.getRemoteSocketAddress()+"下线了");
        }
    }
}

测试
先设置客户端类运行并发启动
在这里插入图片描述
先启动服务端
在这里插入图片描述
启动客户端1,输出
在这里插入图片描述
启动客户端2,输出
在这里插入图片描述
服务端输出:
在这里插入图片描述
当关闭某个客户端时,服务端输出:
在这里插入图片描述


使用线程池优化:
自定义线程池,设置最大线程数量为3
优点:不会引起系统死极,可以控制并发数量
缺点:并发线程数量受到限制

class ServerThreadPool{

    private Executor executor;

    public ServerThreadPool(int maxSize,int queueSize){
        executor=new ThreadPoolExecutor(maxSize,maxSize,120L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(queueSize));
    }

    public void execute(Runnable task){
        executor.execute(task);
    }
}

服务端:

class TCPServer{
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动。。。");
        ServerSocket serverSocket = new ServerSocket(7777);
        //一个服务端对应一个线程池
        ServerThreadPool serverThreadPool = new ServerThreadPool(3, 100);
        while (true){
            Socket socket = serverSocket.accept();
            System.out.println("有人上线了");
            serverThreadPool.execute(new Server(socket));
        }

    }
}

测试:
启动服务端和4个客户端
在这里插入图片描述
四个客户端分别输出 :我是客户A/B/C/D,服务端只会显示3个
在这里插入图片描述
关闭前三个客户端中的一个,服务端打印第四条消息:
在这里插入图片描述

发布了66 篇原创文章 · 获赞 302 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41112238/article/details/104972727