网络通信(三)

本文详细介绍了如何使用Java实现TCP通信,支持多个客户端同时连接,包括Client、Server和ServerReaderThread类的设计,以及如何通过线程池优化服务器性能,展示了BS架构的一个简化实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TCP通信——支持多个客户端同时通信

Client类

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws Exception {
        // 创建Socket对象,并同时请求与服务端程序的连接
        Socket socket = new Socket("127.0.0.1",8888);

        // 从socket通信管道中得到一个字节输出流,用来发送数据给服务端程序
        OutputStream os = socket.getOutputStream();

        // 把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦发现用户输入了exit命令,就退出客户端
            if (msg.equals("exit")){
                System.out.println("退出成功");
                dos.close();
                socket.close();
                break;
            }
            // 开始写数据出去
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

Server类

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("------服务端启动成功------");
        // 创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
            new ServerReaderThread(socket).start();
        }
    }
}

ServerReaderThread类

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (Exception e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

TCP通信—综合案例

即时通信-群聊

Client类

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws Exception {
        // 创建Socket对象,并同时请求与服务端程序的连接
        Socket socket = new Socket("127.0.0.1",8888);

        // 创建一个独立的线程,负责随机从socket中接收服务端发送过来的消息
        new ClientReaderTherad(socket).start();

        // 从socket通信管道中得到一个字节输出流,用来发送数据给服务端程序
        OutputStream os = socket.getOutputStream();

        // 把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦发现用户输入了exit命令,就退出客户端
            if (msg.equals("exit")){
                System.out.println("退出成功");
                dos.close();
                socket.close();
                break;
            }
            // 开始写数据出去
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

Server类

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Server {
    public static List<Socket> onLineSockets = new ArrayList<>();
    public static void main(String[] args) throws Exception {
        System.out.println("------服务端启动成功------");
        // 创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
            Socket socket = serverSocket.accept();
            onLineSockets.add(socket);
            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
            new ServerReaderThread(socket).start();
        }
    }
}

ServerReaderThread类

import java.io.*;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                    // 把这个消息分发给全部客户端进行接收
                    sendMsgToAll(msg);
                } catch (Exception e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    Server.onLineSockets.remove(socket);
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void sendMsgToAll(String msg) throws IOException {
        //发送给全部在线的socket管道接收
        for (Socket onLineSocket : Server.onLineSockets) {
            OutputStream os = onLineSocket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

ClientReaderThread类 

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;

public class ClientReaderTherad extends Thread{
    private Socket socket;
    public ClientReaderTherad(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                    // 把这个消息分发给全部客户端进行接收
                } catch (Exception e) {
                    System.out.println("自己下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

实现一个简易版的BS架构

BS架构的基本原理

 注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据

  •  HTTP协议规定:响应给浏览器的数据格式必须满足如下格式

Server类

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("------服务端启动成功------");
        // 创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true) {
            // 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
            new ServerReaderThread(socket).start();
        }
    }
}

ServerReaderThread类 

import java.io.*;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charest=UTF-8");
            ps.println();
            ps.println("<div style='color:red;font-size:120px;text=align:center'>666<div>");
            ps.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 扩展

每次请求都开一个新的线程,到底好不好?
使用线程池优化

Server类

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Server {
    public static List<Socket> onLineSockets = new ArrayList<>();
    public static void main(String[] args) throws Exception {
        System.out.println("------服务端启动成功------");
        // 创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8080);

        // 创建一个线程池,负责处理通信管道的任务
        ThreadPoolExecutor pool = new ThreadPoolExecutor(8*2,8*2,0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            // 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            pool.execute(new ServerReaderRunnable(socket));
        }
    }
}

ServerReaderRunnable类

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class ServerReaderRunnable implements Runnable{
    private Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charest=UTF-8");
            ps.println();
            ps.println("<div style='color:red;font-size:120px;text=align:center'>666<div>");
            ps.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值