Java基础-网络编程-TCP通信


1. 了解TCP

面相连接,可靠通信,但是通信效率不高
应用场景:网页、文件下载、支付


2. API

构造器说明
public Socket(String host, int port)根据指定服务器的IP地址,端口号请求与服务器建立连接,连接通过,就获得了客户端socket
public ServerSocket(int port)创建服务端对象,并指定端口号
方法说明
public OutputStream getOutputStream()获取字节输出流对象
public InputStream getInputStream()获取字节输入流对象
public Socket accept()阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象

3. 单发单收

  • Client
public class Test4_Client {
    public static void main(String[] args) throws Exception {
        // 创建TCP客户端与服务器建立连接
        Socket socket = new Socket("127.0.0.1",6666);

        // 获取字节输出流发送数据
        OutputStream outputStream = socket.getOutputStream();
        // 把低级字节输出流包装成字节输出输出流
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        // 写入数据
        dataOutputStream.writeUTF("我是TCP客户端,哈哈哈!!!");
        System.out.println("客户端数发送完毕!!!");

        // 关闭数据输出流
        dataOutputStream.close();
        // 关闭socket通信管道
        socket.close();
    }
}
  • Server
public class Test4_Service {
    public static void main(String[] args) throws Exception {
        // 创建服务端对象,并指定端口号
        ServerSocket serverSocket = new ServerSocket(6666);

        // 等待客户端的连接请求【程序暂停,等待客户端连接】--- 如果连接成功,返回的对象就是客户端发送过来的Socket
        Socket socket = serverSocket.accept();

        // 读取客户端发送的数据【从socket通信管道中获取字节输入流】
        InputStream inputStream = socket.getInputStream();
        // 把低级的字节输入流封装成字节数据输入流【这里要与客户端的流保持一致】
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        // 读取数据
        String s = dataInputStream.readUTF();
        System.out.println(s);
        System.out.println("------消息来自于:" + socket.getRemoteSocketAddress() + ":" + socket.getPort() + "------");

        // 关闭资源
        dataInputStream.close();
        socket.close();
    }
}

4. 多发多收【一个客户端对应一个服务端】

  • Client
public class Test5_Client1 {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 6666);

        OutputStream outputStream = socket.getOutputStream();
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.print("请说:");
            String command = scanner.nextLine();

            if ("exit".equals(command)) {
                System.out.println("欢迎下次使用,退出成功!!!");
                dataOutputStream.close();
                socket.close();
                break;
            }

            dataOutputStream.writeUTF(command);
            dataOutputStream.flush();  // 立即发送
        }
    }
}
  • Server
public class Test5_Service {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(6666);

        Socket socket = serverSocket.accept();

        InputStream inputStream = socket.getInputStream();
        DataInputStream dataInputStream = new DataInputStream(inputStream);

        while (true) {
            try {
                String s = dataInputStream.readUTF();
                System.out.println(s);
                System.out.println("------消息来自于:" + socket.getRemoteSocketAddress() + "------");
            } catch (IOException e) {
                System.out.println(socket.getRemoteSocketAddress() + "客户端离线!!!");
                dataInputStream.close();
                socket.close();
                break;
            }
        }
    }
}

5. 多发多收【多个客户端对应一个服务端】

服务器只有一个主线程,只能处理一个客户端的消息,所以需要引入多线程。
只需要更改服务端即可!!!

public class Test6_Service {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(6666);

        while (true) {
            Socket socket = serverSocket.accept();
						
			System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
			
            // 把socket通信管道交给一个独立的线程
            new ServerReaderThread(socket).start();
        }


    }
}

class ServerReaderThread extends Thread {
    private Socket socket;

    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            while (true) {
                try {
                    String s = dataInputStream.readUTF();
                    System.out.println(s);
                    System.out.println("------消息来自于:" + socket.getRemoteSocketAddress() + "------");
                } catch (IOException e) {
                    System.out.println(socket.getRemoteSocketAddress() + "客户端离线!!!");
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6. 群聊【TCP通信-端口转发】

  • Client
public class Client {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 6666);

        // 接收服务器发送的消息
        new ClientReaderThread(socket).start();

        OutputStream outputStream = socket.getOutputStream();
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.println("请说:");
            String command = scanner.nextLine();

            if ("exit".equals(command)) {
                System.out.println("欢迎下次使用,退出成功!!!");
                dataOutputStream.close();
                socket.close();
                break;
            }

            dataOutputStream.writeUTF(command);
            dataOutputStream.flush();  // 立即发送
        }
    }
}
  • ClientReaderThread
public class ClientReaderThread extends Thread{

    private Socket socket;

    public ClientReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            while (true) {
                try {
                    String msg = dataInputStream.readUTF();
                    System.out.println(msg);
                } catch (IOException e) {
                    System.out.println("自己离线!!!");
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • Server
public class Service {
    public static List<Socket> onLineSockets = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(6666);

        while (true) {
            Socket socket = serverSocket.accept();

            onLineSockets.add(socket);  // 增加在线用户
            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 把socket通信管道交给一个独立的线程
            new ServerReaderThread(socket).start();
        }
    }
}
  • ServerReaderThread
public class ServerReaderThread extends Thread {
    private Socket socket;

    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            while (true) {
                try {
                    String msg = dataInputStream.readUTF();
                    System.out.println(msg);
                    System.out.println("------消息来自于:" + socket.getRemoteSocketAddress() + "------");
                    // 把消息分发给全部的客户端进行接收,包括自己
                    sendMsgToAll(msg);
                } catch (IOException e) {
                    System.out.println(socket.getRemoteSocketAddress() + "客户端离线!!!");
                    Service.onLineSockets.remove(socket);  // 删除离线的用户
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendMsgToAll(String msg) throws IOException {
        // 推送给全部在线的客户端
        for (Socket onLineSocket : Service.onLineSockets) {
            OutputStream outputStream = onLineSocket.getOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeUTF(msg);
            dataOutputStream.flush();
        }
    }
}

7. 实现一个简易版的BS架构

  • Server
public class Service {
    public static void main(String[] args) throws IOException {
        System.out.println("------ 服务端启动成功 ------");
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true) {
            Socket socket = serverSocket.accept();

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

            new ServiceReadThread(socket).start();
        }
    }
}
  • ServerReadThread
public class ServiceReadThread extends Thread{
    private Socket socket;

    public ServiceReadThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        // 立刻相应一个网页内容:”人生苦短,我用Java“给浏览器
        try {
            OutputStream outputStream = socket.getOutputStream();

            PrintStream printStream = new PrintStream(outputStream);
            printStream.println("HTTP/1.1 200 OK");
            printStream.println("Content-Type:text/html;charset=UTF-8");
            printStream.println();
            printStream.println("<div style='color:red;font-size:120px;text-align:center'>人生苦短,我用Java</div>");

            printStream.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 测试

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Monly21

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值