Java:网络编程讲解

什么是网络编程?

基本的通信架构:

网络通信的关键三要素:

Java:网络编程讲解_第1张图片

IP:

Java:网络编程讲解_第2张图片

Java:网络编程讲解_第3张图片

计算机访问域名后,会经过dns服务器解析出ip,然后通过ip访问到对应的服务器,然后返回数据展示在浏览器上。

公网IP,内网IP:

特殊的IP地址:

IP常用命令:----win+r 输入cmd进去即可测试使用。

IP常用命令示例:

Java:网络编程讲解_第4张图片

Java:网络编程讲解_第5张图片

InetAddress:

常用方法:

Java:网络编程讲解_第6张图片

代码演示:

package com.itheima.d1_ip;

import java.net.InetAddress;

public class InetAddressTest {
    public static void main(String[] args) throws Exception {
        // 1、获取本机IP地址对象的
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());

        // 2、获取指定IP或者域名的IP地址对象。
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostName());    //主机名www.baidu.com
        System.out.println(ip2.getHostAddress());  //36.155.132.3

        // ping www.baidu.com
        System.out.println(ip2.isReachable(6000));
    }
}

端口:

分类:
Java:网络编程讲解_第7张图片

通信协议:

定义:

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

Java:网络编程讲解_第8张图片

如何快速记住OSI网络参考模型:物  链  网  输  会  表  应

传输层的两个通信协议:

UDP:

UDP通信效率高。

UDP适合语音通话,视频直播等等。

TCP:
Java:网络编程讲解_第9张图片

三次握手:

Java:网络编程讲解_第10张图片

四次挥手:

Java:网络编程讲解_第11张图片

TCP通信效率相对不高。

TCP适合网页、文件下载、支付等等。

UDP通信使用:

Java:网络编程讲解_第12张图片

代码演示(附带讲解):

Client类代码:

package com.itheima.d2_upd1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * 目标:完成UDP通信快速入门:实现1发1收。
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */
        byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();//要转成字节数组才行
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                , InetAddress.getLocalHost(),  6666);

        // 3、开始正式发送这个数据包的数据出去了
        socket.send(packet);

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

Server类代码:
 

package com.itheima.d2_upd1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象 注册端口
        DatagramSocket socket = new DatagramSocket(6666);

        // 2、创建一个数据包对象,用于接收数据的
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        // 3、开始正式使用数据包来接收客户端发来的数据
        socket.receive(packet);

        // 4、从字节数组中,把接收到的数据直接打印出来
        // 接收多少就倒出多少
        // 获取本次数据包接收了多少数据。
        int len = packet.getLength();

        String rs = new String(buffer, 0 , len);
        System.out.println(rs);

        System.out.println(packet.getAddress().getHostAddress());  //客户端的IP
        System.out.println(packet.getPort());    //获取客户端的端口

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


}

我们必须先启动Server程序,再启动Client程序,经过测试可以成功一发一收。

如何实现一个客户端多发呢?

可以把代码更改如下:

Client类代码:

package com.itheima.d3_udp2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦发现用户输入的exit命令,就退出客户端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临!退出成功!");
                socket.close(); // 释放资源
                break; // 跳出死循环
            }

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

            // 3、开始正式发送这个数据包的数据出去了
            socket.send(packet);
        }
    }
}

Server类代码也要改动:
 

package com.itheima.d3_udp2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象 注册端口
        DatagramSocket socket = new DatagramSocket(6666);

        // 2、创建一个数据包对象,用于接收数据
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        while (true) {
            // 3、开始正式使用数据包来接收客户端发来的数据
            socket.receive(packet);

            // 4、从字节数组中,把接收到的数据直接打印出来
            // 接收多少就倒出多少
            // 获取本次数据包接收了多少数据。
            int len = packet.getLength();

            String rs = new String(buffer, 0 , len);
            System.out.println(rs);

            System.out.println(packet.getAddress().getHostAddress());
            System.out.println(packet.getPort());
            System.out.println("--------------------------------------");
        }
    }
}

这个程序可以实现一个程序不断发送,服务端不断接受数据包。

还有一个问题,这里只有一个客户端,实际情况下不止一个客户端,我们该怎么启动多个客户端来测试这个程序呢?

找到下图位置:----箭头所指处

点击Edit:

进去后是下图模样:

Java:网络编程讲解_第13张图片

找到:

勾选Allow。。。。这一行:

Java:网络编程讲解_第14张图片

然后点击Apply应用,点击ok即可。

这个时候你就可以多次启动Client类,获得多个客户端来测试了。

我已经测试过,正常运行。

TCP通信使用:

客户端开发所使用的构造器和常用方法:

Java:网络编程讲解_第15张图片

服务端开发所使用的构造器和常用方法:

Java:网络编程讲解_第16张图片

创建客户端类:

package com.itheima.d5_tcp2;

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

/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

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

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

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

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }

            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

创建服务端类:

package com.itheima.d5_tcp2;

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

/**
 *  目标:完成TCP通信快速入门-服务端开发:实现服务端反复发消息
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

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

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        while (true) {
            try {
                // 5、使用数据输入流读取客户端发送过来的消息
                String rs = dis.readUTF();
                System.out.println(rs);
            } catch (Exception e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线了!");
                dis.close();
                socket.close();
                break;
            }
        }
    }
}













先启动服务类再启动客户端类,经过测试是可以正常运行的。

但是,这里面临一个问题,它是否能够做到多个客户端发送信息,服务端接收?

这个跟UDP是不一样的,上面的代码,如果我们启动多个客户端去测试,我们发现只能有一个客户端可以正常发送数据包到服务端,其它的客户端都失效了。

为什么?

原因:我们通过.accepted方法等待客户端的连接请求,如果第一个客户端连接上,我们就进入while死循环了,其它客户端启动后发送数据包,服务端是收不到的。

如何解决?

使用多线程:

Java:网络编程讲解_第17张图片

具体代码演示:

Client类:

package com.itheima.d6_tcp3;

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

/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);
        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();
        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

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

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }
            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

ServerReaderThread类:

package com.itheima.d6_tcp3;

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();
        }
    }
}

Server类:

package com.itheima.d6_tcp3;

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

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

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

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

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













通过测试,可以正常运行。

TCP通信:群聊

群聊功能要用到TCP通信-端口转发:----原理图

Java:网络编程讲解_第18张图片

代码演示:
 

Client类:

package com.itheima.d7_tcp4;

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 {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

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

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();
        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

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

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }
            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

ClientReaderThread类:

package com.itheima.d7_tcp4;

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

public class ClientReaderThread extends Thread {
    private Socket socket;
    public ClientReaderThread(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();
        }
    }
}

Server类:

package com.itheima.d7_tcp4;

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

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

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();
            onLineSockets.add(socket);
            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }
    }
}

ServerReaderThread类:

package com.itheima.d7_tcp4;

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();
        }
    }
}

通过测试,我们启动了2个客户端,其中一个客户端发信息,服务端和其余的客户端都能看到,并且自己也能看到,证明这个程序是可以正常运行的。

TCP通信实现BS架构:

我们只需要写服务端,客户端就是浏览器,不需要我们去写客户端了。

需求:
要求从浏览器中访问服务器并立即让服务器响应一个很简单的网页给浏览器展示,网页内容就是“黑马程序员666”。

代码示例:
 

Server类:

package com.itheima.d8_tcp5;

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

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

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

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

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

ServerReaderThread类:

package com.itheima.d8_tcp5;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
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();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF("黑马程序员666");
            dos.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

启动程序后,我们打开浏览器,输入127.0.0.1:8080,结果图:

Java:网络编程讲解_第19张图片

我们发现竟然访问不了,为什么呢?

原因:

Java:网络编程讲解_第20张图片

解决办法:

只需要修改ServerReaderThread类:

package com.itheima.d8_tcp5;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
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();
            DataOutputStream dos = new DataOutputStream(os);
            /*dos.writeUTF("黑马程序员666");
            dos.close();*/
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须换行
            ps.println("
黑马程序员666
"); ps.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } }

启动程序后再次打开浏览器测试,输出图:

Java:网络编程讲解_第21张图片

可以看到,可以正常输出。

你可能感兴趣的:(java,服务器,java,tomcat,spring,maven,intellij-idea)