Java 网络通信、UDP通信、TCP通信

一. 网络编程

        1. 网络编程:可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信)

        2. Java.net.*包下提供了网络编程的解决方法

二. 基本的通信架构

        1. CS架构(Clicnt客户端/Server服务端);如微信

        2. BS架构(Browser浏览器/Server服务端);如浏览器里面的网页

三. 网络通信三要素

        1. IP地址

                ① IP(Internet Protocol):全称"互联网协议地址",设备在网络中的地址,是唯一的标识

                ② IP地址有两种形式:IPv4、IPv6

                

                ③ 公网IP: 是可以连接互联网的IP地址;内网IP:也叫局域网IP,只能组织机构内部使用

                ④ 127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机

                ⑤ IP常用命令:

                        ipconfig:查看本机IP地址;ping IP地址:检查网络是否连通;

                ⑥ Java InetAddress类:代表IP地址

InetAddress的常用方法说明
public static InetAddress getLocalHost()获取本机IP,会以一个inetAddress的对象返回
public static InetAddress getByName(String host)根据IP地址或者域名,返回一个inetAddress对象
public String getHostName()获取该IP地址对象对应的主机名
public String getHostAddress()获取该IP地址对象中的IP地址信息
public boolean isReadchable(int timeout)在指定毫秒内,判断主机与该IP地址是否能连通

        

public static void main(String[] args) throws Exception {
        //public static InetAddress getLocalHost()	获取本机IP,会以一个inetAddress的对象返回
        InetAddress address = InetAddress.getLocalHost();
        System.out.println(address);
        //public String getHostName()	获取该IP地址对象对应的主机名
        System.out.println(address.getHostName());
        //public String getHostAddress()	获取该IP地址对象中的IP地址信息
        System.out.println(address.getHostAddress());

        //public static InetAddress getByName(String host)	根据IP地址或者域名,返回一个inetAddress对象
        InetAddress address2 = InetAddress.getByName("www.baidu.com");
        System.out.println(address2);
        System.out.println(address2.getHostName());
        System.out.println(address2.getHostAddress());

        //public boolean isReadchable(int timeout)	在指定毫秒内,判断主机与该IP地址是否能连通
        System.out.println(address2.isReachable(100));
}

        2. 端口号

                应用程序在设备中的唯一标识;标记正在计算机设备上运行的应用程序的,被规定为1个16位的二进制,范围是0-65535。

                分类:

                        周知端口:0-1023,被预先定义的知名应用占用(如HTTP占用80,FTP占用21)

                        注册端口:1024-49151,分配给用户进程或某些应用程序

                        动态端口:49152-65535,一般不固定分配给某种进程,进行动态分配

                我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则会出错

        3. 协议

                通信协议:网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议;

                开放式网络互联标准:OSI网络参考模型

                TCP/IP网络模型:事实上国际标准

          传输层的两个通信协议:

                ① UDP(User Datagram Protocol):用户数据报协议;

                        特点:无连接、不可靠通信、通信效率高(语音通话、视频直播);不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口、目的地IP、程序端口和数据(限制在64KB内)等;发送方不管对方是否在线,数据在中间是否丢失,如果接收方收到数据也不返回确认,所以是不可靠的。

                ② TCP(Transmission Control Protocol):传输控制协议

                        特点:面向连接、可靠通信。TCP的最终目的:要保证在不可靠的信道上实现可靠的传输;三个步骤: 三次握手建立连接;传输数据进行确认;四次挥手断开连接

                        三次握手建立连接:确定通信双方,收发消息都是正常无问题的!(全双工)

                        传输数据进行确认:

                        四次挥手断开连接:确保双方数据的收发都已经完成。

四. UDP通信

        1. 特点:无连接、不可靠通信、通信效率高(语音通话、视频直播);不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口、目的地IP、程序端口和数据(限制在64KB内)等;发送方不管对方是否在线,数据在中间是否丢失,如果接收方收到数据也不返回确认,所以是不可靠的

        2. Java提供了一个Java.net.DatagramSocket类来实现UDP通信。

        DatagramSocket:用于创建客户端、服务端

构造器说明
public DatagramSocket()创建客户端的Socket对象,系统会随机分配一个端口号
public DatagramSocket(int port)创建服务端的Socket对象,并指定端口号
方法说明
public void send(DatagramPacket dp)发送数据包
public void receive(DatagramPacket p)使用数据包接收数据

          DatagramPacket:创建数据包

构造器说明
public DatagramPacket(byte[] buf, int length, InetAddress address, int port)创建发出去的数据包对象
public DatagramPacket(byte[] buf, int length)创建用来接收数据的数据包
方法说明
public int getLength()获取数据包,实际接收到的字节个数

        一发一收

/*
 * UDP-客户端 一发一收
 * */
public class Client {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //1. 创建客户端对象
        //public DatagramSocket()	创建客户端的Socket对象,系统会随机分配一个端口号
        DatagramSocket socket = new DatagramSocket();

        //2. 创建数据包对象 封装要发出去的数据
        //public DatagramPacket(byte[] buf, int length, InetAddress address, int port)	创建发出去的数据包对象
        /*
        * byte[] buf 要发出去的数据
        * int length 发出去的数据大小
        * InetAddress address 服务端的IP地址 找到服务端主机
        * int port 服务端的IP地址 的端口号
        * */
        byte[] buf ="卡莎:艾卡西亚暴雨".getBytes();
        DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 9999);

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

        System.out.println("客户端发送数据完毕");
        //4. 释放资源
        socket.close();
    }
}




/*
* UDP-服务端 一发一收
* */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //1. 创建服务端对象
        //public DatagramSocket(int port)	创建服务端的Socket对象,并指定端口号
        DatagramSocket socket = new DatagramSocket(9999);

        //创建数据包对象 接收数据
        //public DatagramPacket(byte[] buf, int length)	创建用来接收数据的数据包
        byte[] buf = new byte[1024*64];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

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

        //4.打印接收到的数据
        int len = packet.getLength();
        String rs = new String(buf, 0, len);
        System.out.println(rs);

        //获取客户端的IP地址
        System.out.println(packet.getAddress());
        //获取客户端的端口
        System.out.println(packet.getPort());
        //5.关闭
        socket.close();
    }
}

         多发多收      

                

/*
 * UDP-客户端 多发多收
 * */
public class Client1 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //1. 创建客户端对象
        //public DatagramSocket()	创建客户端的Socket对象,系统会随机分配一个端口号
        DatagramSocket socket = new DatagramSocket();

        //2. 创建数据包对象 封装要发出去的数据
        //public DatagramPacket(byte[] buf, int length, InetAddress address, int port)	创建发出去的数据包对象
        /*
        * byte[] buf 要发出去的数据
        * int length 发出去的数据大小
        * InetAddress address 服务端的IP地址 找到服务端主机
        * int port 服务端的IP地址 的端口号
        * */
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入消息:");
            String msg = scanner.nextLine();

            //输入“exit”表示退出
            if ("exit".equals(msg)) {
                System.out.println("退出成功");
                socket.close();
                break;
            }

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

            //3. 开始发送数据包
            socket.send(packet);
        }
    }
}




/*
* UDP-服务端 多发多收
* */
public class Server1 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //1. 创建服务端对象
        //public DatagramSocket(int port)	创建服务端的Socket对象,并指定端口号
        DatagramSocket socket = new DatagramSocket(9999);

        //创建数据包对象 接收数据
        //public DatagramPacket(byte[] buf, int length)	创建用来接收数据的数据包
        byte[] buf = new byte[1024*64];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        while (true) {
            //3. 接收数据
            socket.receive(packet);

            //4.打印接收到的数据
            int len = packet.getLength();
            String rs = new String(buf, 0, len);
            System.out.println(rs);

            //获取客户端的IP地址
            System.out.println(packet.getAddress());
            //获取客户端的端口
            System.out.println(packet.getPort());
            System.out.println("----------------------");
        }
    }
}

五. TCP通信 

        特点:面向连接、可靠通信。通信双方事先会采用“三次握手”方式来建立连接,实现端到端的通信;底层能保证数据成功传给服务端

        客户端:Java.net.Socket类实现TCP通信。

客户端构造器说明
public Socket(String host, int port)根据指定的服务器IP、端口号请求与服务端建立连接,连接通过,就获得了客户端的socket
方法说明
public OutputStream getOutputStream()获得字节输出流对象
public InputStream getInputStream()获得字节输入流对象

        服务端:Java.net.ServerSocket类实现

构造器说明
public ServerDocket(int port)为服务端程序注册端口
方法
public Socket accept阻塞等待客户端的连接请求,一旦与某个客户端连接成功,则返回服务端的Socket对象

        一发一收

/*
 * TCP 服务端 一发一收
 * */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //创建服务端对象
        ServerSocket serverSocket = new ServerSocket(9999);

        //等待客户端连接请求
        Socket socket = serverSocket.accept();

        //接收客户端的数据
        InputStream inputStream = socket.getInputStream();
        //把原始的字节输入流包装成数据输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        String str = dataInputStream.readUTF();
        System.out.println(str);

        //获取客户端的IP地址
        System.out.println(socket.getRemoteSocketAddress());

        //释放资源
        dataInputStream.close();
        socket.close();
    }
}



/*
* TCP 客户端 一发一收
* */
public class Client {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //创建Socket对象
        Socket socket = new Socket("127.0.0.1", 9999);

        //字节输出流 发送数据
        OutputStream os = socket.getOutputStream();
        //把低级字节输出流包装成高级的数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        dos.writeUTF("卡莎、;艾卡西亚");

        dos.close();
        socket.close();
        System.out.println("消息发送成功");
    }
}

        多发多收 

/*
 * TCP 服务端 多发多收
 * */
public class Server1 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //创建服务端对象
        ServerSocket serverSocket = new ServerSocket(9999);

        //等待客户端连接请求
        Socket socket = serverSocket.accept();

        //接收客户端的数据
        InputStream inputStream = socket.getInputStream();
        //把原始的字节输入流包装成数据输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);

        while (true) {
            try {
                String str = dataInputStream.readUTF();
                System.out.println(str);
            } catch (IOException e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线了");
                socket.close();
                dataInputStream.close();
                break;
            }
        }
    }
}


/*
* TCP 客户端 多发多收
* */
public class Client1 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //创建Socket对象
        Socket socket = new Socket("127.0.0.1", 9999);

        //字节输出流 发送数据
        OutputStream os = socket.getOutputStream();
        //把低级字节输出流包装成高级的数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入消息");
            String msg = sc.nextLine();

            if("exit".equals(msg)){
                System.out.println("退出成功");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(msg);
            dos.flush();
        }

    }
}

与多个客户端同时通信

        上面的服务端程序只能支持一个客户端通信;因为服务端只有一个线程,只能处理一个客户端的消息。

        使用多线程与多个客户端同时通信


/*
* 线程类
* */
public class ServerReaderThread extends Thread {
    private Socket socket;

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


        try {
            //接收客户端的数据
            InputStream inputStream = socket.getInputStream();
            //把原始的字节输入流包装成数据输入流
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            while (true) {
                try {
                    String str = dataInputStream.readUTF();
                    System.out.println(str);
                } catch (IOException e) {
                    System.out.println(socket.getRemoteSocketAddress()  + "下线了");
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}



/*
 * TCP 服务端 多线程-服务端支持与多个客户端通信
 * */
public class Server2 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //创建服务端对象
        ServerSocket serverSocket = new ServerSocket(9999);

        while (true) {
            //等待客户端连接请求
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress() + "上线了");
           //把这个客户端对应的socket通信管道,交给一个独立的线程
            new ServerReaderThread(socket).start();
        }
    }
}



/*
* TCP 客户端 多线程-服务端支持与多个客户端通信
* */
public class Client2 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //创建Socket对象
        Socket socket = new Socket("127.0.0.1", 9999);

        //字节输出流 发送数据
        OutputStream os = socket.getOutputStream();
        //把低级字节输出流包装成高级的数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入消息");
            String msg = sc.nextLine();

            if("exit".equals(msg)){
                System.out.println("退出成功");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(msg);
            dos.flush();
        }

    }
}

六. 群聊

       TCP通信-端口转发

        多个客户端与客户端通信

/*
* 群聊-服务端-线程类
* */
public class ServerReaderThread extends Thread {
    private Socket socket;

    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }
    public void run() {
        try {
            //接收客户端的数据
            InputStream inputStream = socket.getInputStream();
            //把原始的字节输入流包装成数据输入流
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            while (true) {
                try {
                    String str = dataInputStream.readUTF();
                    System.out.println(str);
                    sendAllOnLine(str);
                    //把消息分发个全部客户端
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println(socket.getRemoteSocketAddress()  + "下线了");
                    Server3.onLineSockets.remove(socket);
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void sendAllOnLine(String str) throws IOException {
        for (Socket socket : Server3.onLineSockets) {
            OutputStream outputStream = socket.getOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeUTF(str);
            dataOutputStream.flush();
        }
    }
}



/*
 * 群聊-客户端-线程类接收消息
 * */
public class ClentReaderThread extends Thread {
    private Socket socket;

    public ClentReaderThread(Socket socket) {
        this.socket = socket;
    }
    public void run() {


        try {
            //接收服务端的数据
            InputStream inputStream = socket.getInputStream();
            //把原始的字节输入流包装成数据输入流
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            while (true) {
                try {
                    String str = dataInputStream.readUTF();
                    System.out.println(str);
                } catch (IOException e) {
                    System.out.println("自己下线了");
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}



/*
 * TCP 服务端 群聊
 * */
public class Server3 {
    public static ArrayList<Socket> onLineSockets = new ArrayList<>();
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //创建服务端对象
        ServerSocket serverSocket = new ServerSocket(9999);

        while (true) {
            //等待客户端连接请求
            Socket socket = serverSocket.accept();
            onLineSockets.add(socket);
            System.out.println(socket.getRemoteSocketAddress() + "上线了");
           //把这个客户端对应的socket通信管道,交给一个独立的线程
            new ServerReaderThread(socket).start();
        }
    }
}


/*
* TCP 客户端 群聊
* */
public class Client3 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //创建Socket对象
        Socket socket = new Socket("127.0.0.1", 9999);

        //创建线程接收服务端的消息
        new ClentReaderThread(socket).start();

        //字节输出流 发送数据
        OutputStream os = socket.getOutputStream();
        //把低级字节输出流包装成高级的数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入消息");
            String msg = sc.nextLine();

            if("exit".equals(msg)){
                System.out.println("退出成功");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(msg);
            dos.flush();
        }

    }
}

七. 简易版的BS架构

        简易版的BS架构

                BS架构的基本原理:http://服务器IP:服务器端口(本地-http://127.0.0.1:8080)

/*
* 服务端-线程类
* */
public class ServerReaderThread extends Thread {
    private Socket socket;

    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }
    public void run() {
        //立即响应
        try {
            OutputStream outputStream = socket.getOutputStream();
            //服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.println("HTTP/1.1 200 OK");
            printWriter.println("Content-Type: text/html; charset=utf-8");
            printWriter.println();//必须换行
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<title>嘿嘿嘿</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h1>你真帅</h1>");

            printWriter.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}


/*
 * TCP 服务端 简易版BS架构
 * */
public class Server2 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //创建服务端对象
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true) {
           
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress() + "上线了");
          
            new ServerReaderThread(socket).start();
        }
    }
}

        使用线程池进行优化

/*
* 服务端-任务类
* */
public class ServerReaderRunnable implements Runnable {
    private Socket socket;

    public ServerReaderRunnable(Socket socket) {
        this.socket = socket;
    }
    public void run() {
        //立即响应
        try {
            OutputStream outputStream = socket.getOutputStream();
            //服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.println("HTTP/1.1 200 OK");
            printWriter.println("Content-Type: text/html; charset=utf-8");
            printWriter.println();//必须换行
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<title>嘿嘿嘿</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h1>你真帅</h1>");

            printWriter.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}


/*
 * TCP 服务端 简易版BS架构-使用线程池优化
 * */
public class Server3 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        //创建服务端对象
        ServerSocket serverSocket = new ServerSocket(8080);

        //创建线程池
        ThreadPoolExecutor pool =  new ThreadPoolExecutor(12 * 2, 12 * 2, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            //等待客户端连接请求
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress() + "上线了");
           //把这个客户端对应的socket通信管道,交给一个独立的线程
           pool.execute(new ServerReaderRunnable(socket));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值