Java的网络通信

网络通信概述

网络通信编程的三要素:①IP地址 ②端口号 ③协议

IP常用命令 说明
ipconfig 查看本机ip地址
ping IP地址 检查网络是否连通

本机ip:127.0.0.1 or localhost

IP地址操作类:InetAddress

InetAddress类API

UDP通信

无连接、不可靠传输,UDP通信模型类似于传菜(盘子+菜),一次最多传输64KB

DatagramPacket(韭菜盘子)

DatagramPacket

DatagramSocket(收发双方对象)

DatagramSocket

UDP单发单收

接收端

public class UDPSever {
    public static void main(String[] args) throws Exception {
        //1.创建接收端对象
        DatagramSocket socket = new DatagramSocket(8888);

        //2.创建接收端数据包(盘子)
        byte[] bytes = new byte[1024 * 64];  //64KB
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);

        //3.接受数据
        socket.receive(packet);

        //4.多余数据的裁剪
        String string = new String(bytes,0, packet.getLength());
        System.out.println("收到了:" + string);

        //5.关闭套接字
        socket.close();
    }
}

发送端

public class UDPClient {
    public static void main(String[] args) throws Exception {
        //1.创建客户端对象(不声明端口则为默认端口号)
        DatagramSocket socket = new DatagramSocket();

        //2.创建客户端数据包(盘子)
        byte[] bytes = "hello!this is my first udp program!".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8888);

        //3.发送数据
        socket.send(packet);

        //4.关闭套接字
        socket.close();
    }
}

UDP多发多收

收发加循环

接收端

public class UDPSever {
    public static void main(String[] args) throws Exception {
        //1.创建接收端对象
        DatagramSocket socket = new DatagramSocket(8888);

        //2.创建接收端数据包(盘子)
        byte[] bytes = new byte[1024 * 64];  //64KB
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);

        //3.接受数据
        while (true) {
            socket.receive(packet);
            //4.多余数据的裁剪
            String string = new String(bytes,0, packet.getLength());
            System.out.println("收到了来自" + packet.getAddress() + "的消息:" + string);
        }
    }
}

发送端

public class UDPClient {
    public static void main(String[] args) throws Exception {
        //1.创建客户端对象(不声明端口则为默认端口号)
        DatagramSocket socket = new DatagramSocket();

        Scanner scanner = new Scanner(System.in);

        while(true) {
            System.out.println("发送内容:");
            String msg = scanner.nextLine();

            if("exit".equals(msg)) {
                System.out.println("退出聊天室");
                break;
            }

            byte[] bytes = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8888);

            socket.send(packet);
        }

        //4.关闭套接字
        socket.close();
    }
}

UDP广播、组播

UDP广播

通过将发送方的目的地址设置为255.255.255.255(广播地址)来实现广播消息

UDP组播

组播地址:224.0.0.0 ~239.255.255.255

组播地址绑定:通过DatagramSocket的子类MulticastSocket绑定组播ip

//1.创建多播对象
MulticastSocket socket = new MulticastSocket(8888);
//2.加入多播组
socket.joinGroup(InetAddress.getByName("225.123.123.123"));

TCP通信

面向连接、安全、可靠,点对点通信,可进行大数据量传输,使用java.net.Socket类实现通信,Socket类将IO操作结合,IO的写操作即是数据的传输操作

TCP一发一收

客户端

public class TCPClient {
    public static void main(String[] args) {
        try {
            //创建发送端对象
            Socket socket = new Socket("127.0.0.0",8888);
            //获取输出流
            OutputStream stream = socket.getOutputStream();
            //将字节流升级为打印流
            PrintStream ps = new PrintStream(stream);
            //发送数据
            ps.print("hello my friend!");
            //刷新缓冲区(必须)
            ps.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端

ServerSocket类注册端口

public class TCPServer {
    public static void main(String[] args) {
        try {
            //1.建立ServerSocket服务端,注册端口
            ServerSocket serverSocket = new ServerSocket(7777);
            //2.等待客户端连接,获取客户端对象
            Socket socket = serverSocket.accept();
            //3.实例化一个字节输入流
            InputStream inputStream = socket.getInputStream();
            //4.将字节输入流包装成更高级的缓冲字符输入流(字节输入流->转换流->缓冲字符输入流)
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            String msg;
            if((msg = reader.readLine()) != null) {
                System.out.println(socket.getInetAddress() + "发送了:" + msg);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

TCP多发多收

为了实现接收多个客户端的消息,需要使用多线程技术

客户端

public class TCPClient {
    public static void main(String[] args) {
        try {
            //1.创建发送端对象
            Socket socket = new Socket("127.0.0.1",7777);
            Scanner scanner = new Scanner(System.in);
            //2.获取输出流
            OutputStream stream = socket.getOutputStream();
            //3.将字节流升级为打印流
            PrintStream ps = new PrintStream(stream);
            //4.实现多发功能
            while (true) {
                System.out.println("请发送:");
                //发送数据
                ps.println(scanner.nextLine());
                //刷新缓冲区
                ps.flush();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端

public class TCPServer {
    public static void main(String[] args) {
        try {
            //1.建立ServerSocket服务端,注册端口
            ServerSocket serverSocket = new ServerSocket(7777);
            //2.实现多收(多线程)
            while (true) {
                //3.等待连接
                Socket socket = serverSocket.accept();
                System.out.println(socket.getInetAddress() + "加入了");
                //4.启动线程
                new work(socket).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class work extends Thread {
    private Socket socket;
    //传入socket对象
    public work(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream inputStream = null;
        try {
            inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            //5.收发消息
            while (true) {
                if ((msg = reader.readLine()) != null) {
                    System.out.println(socket.getInetAddress() + "发送了:" + msg);
                }
            }
        } catch (Exception ex) {
            System.out.println(socket.getInetAddress() + "退出了");
        }
    }
}

TCP多发多收的优化——线程池

防止开启线程过多,可以使用线程池来创建线程。适合客户端通信时长较短的场景。仅需要修改服务端

服务端

public class TCPServer {
    //定义一个静态线程池
    private static ExecutorService pool = new ThreadPoolExecutor(3, 5,
            6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2) );

    public static void main(String[] args) {
        try {
            //1.建立ServerSocket服务端,注册端口
            ServerSocket serverSocket = new ServerSocket(7777);
            //2.实现多收(多线程)
            while (true) {
                //3.等待连接
                Socket socket = serverSocket.accept();
                System.out.println(socket.getInetAddress() + "加入了");
                //4.启动线程
                Runnable run = new Pools(socket);
                pool.execute(run);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Pools implements Runnable {
    private Socket socket;
    public Pools(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream inputStream = null;
        try {
            inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            //5.收发消息
            while (true) {
                if ((msg = reader.readLine()) != null) {
                    System.out.println(socket.getInetAddress() + "发送了:" + msg);
                }
            }
        } catch (Exception ex) {
            System.out.println(socket.getInetAddress() + "退出了");
        }
    }
}

TCP项目实战

即时通信

即时通讯是指客户端可以接收到其他客户端发送的信息,思想是存储管道进行端口转发

客户端

public class TCPClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1",7777);
            Scanner scanner = new Scanner(System.in);
            OutputStream stream = socket.getOutputStream();
            PrintStream ps = new PrintStream(stream);
            //1.开启客户端的读线程
            new ReadThread(socket).start();
            while (true) {
                System.out.println("请发送:");
                ps.println(scanner.nextLine());
                ps.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ReadThread extends Thread {
    private Socket socket;
    public ReadThread(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream inputStream = null;
        try {
            inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            while (true) {
                if ((msg = reader.readLine()) != null) {
                    System.out.println("收到了:" + msg);
                }
            }
        } catch (Exception e) {
            System.out.println("服务器已将你踢出群聊");
        }
    }
}

服务端

public class TCPServer {
    private static ExecutorService pool = new ThreadPoolExecutor(3, 5,
            6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2) );
    //1.创建一个列表用于存放所有客户端socket对象
    public static List<Socket> socketList = new ArrayList<>();

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(7777);
            while (true) {
                Socket socket = serverSocket.accept();
                //2.将加入的客户端socket对象加入列表
                socketList.add(socket);
                System.out.println(socket.getInetAddress() + "加入了");
                Runnable run = new Pools(socket);
                pool.execute(run);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Pools implements Runnable {
    private Socket socket;
    public Pools(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream inputStream = null;
        try {
            inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            //3.转发信息给其他客户端
            while (true) {
                if ((msg = reader.readLine()) != null) {
                    for (Socket socket1 : TCPServer.socketList) {
                        OutputStream os = socket1.getOutputStream();
                        PrintStream ps = new PrintStream(os);
                        ps.println(msg);
                        ps.flush();
                    }
                }
            }
        } catch (Exception ex) {
            //4.在列表中删除退出的客户端信息
            TCPServer.socketList.remove(socket);
            System.out.println(socket.getInetAddress() + "退出了");
        }
    }
}

BS结构(了解)

BS结构不同于CS结构,BS结构不需要开发客户端,是通过浏览器去访问服务端

BS服务器必须返回HTTP协议的报文

posted @ 2022-08-05 23:06  科班小白  阅读(284)  评论(0编辑  收藏  举报