自学内容网 自学内容网

Java基础14-网络编程

十四、网络编程

java.net.*包下提供了网络编程的解决方案!

基本的通信架构

基本的通信架构有2种形式: CS架构( Client客户端/Server服务端)、BS架构(Browser浏 览器/Server服务端)。无论是CS架构,还是BS架构的软件都必须依赖网络编程!。

1、网络通信的三要素

网络通信的三要素:IP地址、端口、协议。

1.1 InetAddress

代表IP地址,是一个操作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 isReachable(int timeout )在指定毫秒内,判断主机与该IP对应的主机是否能连通
public class InetAddressTest   {
    public static void main(String[] args) throws Exception {
        //获取本机IP地址对象
        InetAddress  localhost=InetAddress.getLocalHost();
        //获取指定IP对象的IP地址
        String   lip=  localhost.getHostAddress();
        //获取指定IP对象的主机名
        String   name=localhost.getHostName();
        System.out.println(lip);
        System.out.println(name);

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

        //判断指定IP对象在1秒内是否与本机是否可以连通
        boolean  isComm=baidu.isReachable(1000);
        System.out.println(isComm);
    }
}
1.2、端口

端口号:标记正在计算机设备.上运行的应用程序的,被规定为-个16位的二进制,范围是0~65535。

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

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

动态端口: 49152到65535,之所以称为动态端口,是因为它-般不固定分配某种进程,而是动态分配。

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

1.3、协议

开放式网络互联标准::OSI网络参考模型,是全球网络互联标准。

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

UDP(User Datagram Protocol): 用户数据报协议; TCP(Transmission Control Protocol) :传输控制协议。

在这里插入图片描述

1.3.1 UDP协议

特点::通信效率搞、无连接、不可靠通信。

不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内),超过就分包发送。

发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的。

场景:视频语音通话、网络直播。

1.3.2、TCP协议

特点:通信效率相对不高、面向连接、可靠通信。

TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。

TCP主要有三个步 骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。

场景:文件下载、网页、支付。

2、UDP通信

特点:无连接、不可靠通信。

不事先建立连接,发送端每次把要发送的数据(限制在64KB内)、接收端IP、 等信息封装成一个数据包, 发出去就不管了。

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

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

构造器说明
public DatagramSocket()创建客户端的Socket对象,系统会随机分配一个端口号
public DataGramSocket( int port )创建服务器的Socket对象,并指定端口号
DatagramSocket对象提供的方法说明
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()获取数据包,实际接收到的字节个数

1、客户端向服务端发送UDP信息——一发一收

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

        //2、准备数据,将字符串转为字符数组
        byte[]  hi= "你好Java,Hello Java".getBytes();
        //3、创建发送数据的数据包,参数:(发送数据的字节数组,数据字节大小,接收方的IP地址对象(这里是本机),接收方开放的端口)
        DatagramPacket packet=new DatagramPacket(hi,hi.length, InetAddress.getLocalHost(),6060);
        //4、调用客户端对象的发送方法,将数据包对象作为参数,发送数据。
        socket.send(packet);
        //5、关闭发送接口
        socket.close();
         System.out.println("客户端发送完毕!");
    }
}
//服务端
public class Service {
    public static void main(String[] args) throws Exception {
        //1、创建服务端的对象(端口号要和客户端一样)
        DatagramSocket socket=new DatagramSocket(6060);
        System.out.println("-----服务端启动----");
        //限制接收数据的字节数(最大64kb)
        byte[]  buffer=new byte[1024*64];
        //2、创建接收数据包的对象,参数(接收字节的数组,限制接收字节的大小)
        DatagramPacket packet=new DatagramPacket(buffer,buffer.length);
        //3、通过服务端对象的方法接收数据
        socket.receive(packet);
        //4、获取到接收的数据
         //获取接收数据的大小
        int   len=packet.getLength();
        //由于用来装数据的数组是最大来算的(64kb),很可能装不满,所有接收多少,倒多
        String str=new String(buffer,0,len);
        System.out.println(str);
          //关闭服务端
        socket.close();
      
    }
}

2、客户端向服务端发送UDP信息——用户控制多次发送

使用死循环来实现

//客户端
public class Client {
    public static void main(String[] args) throws Exception {
        Scanner  scanner=new Scanner(System.in);
        //1、获取客户端对象
        DatagramSocket  socket=new DatagramSocket();

        while (true) {
            System.out.print("请输入发送内容:");
            //2、准备数据,将字符串转为字符数组
            String say= scanner.next();
            if (!say.equals("0")) {
                //3、创建发送数据的数据包,参数:(发送数据的字节数组,数据字节大小,接收方的IP地址对象(这里是本机),接收方开放的端口)
                DatagramPacket packet=new DatagramPacket(say.getBytes(),say.getBytes().length, InetAddress.getLocalHost(),6060);
                //4、调用客户端对象的发送方法,将数据包对象作为参数,发送数据。
                socket.send(packet);
                System.out.println("已发送!");
            }else {
                break;
            }
        }
        //5、关闭发送接口
        socket.close();
    }
}
//服务端
public class Service {
    public static void main(String[] args) throws Exception {
        //1、创建服务端的对象(端口号要和客户端一样)
        DatagramSocket socket=new DatagramSocket(6060);
        System.out.println("-----服务端启动----");

        //限制接收数据的字节数(最大64kb)
        byte[]  buffer=new byte[1024*64];
        while (true) {
            //2、创建接收数据包的对象,参数(接收字节的数组,限制接收字节的大小)
            DatagramPacket packet=new DatagramPacket(buffer,buffer.length);
            //3、通过服务端对象的方法接收数据
            socket.receive(packet);
            //4、获取到接收的数据
            //获取接收数据的大小
            int   len=packet.getLength();
            //由于用来装数据的数组是最大来算的(64kb),很可能装不满,所有接收多少,倒多
            String str=new String(buffer,0,len);
            System.out.println("已接收内容:"+str);
        }
        //关闭服务端
       // socket.close();
    }
}

3、TCP通信

特点:面向连接、可靠通信。

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

1、TCP客户端开发

Java提供了一个java.net.Socket类 来实现TCP通信。

Socket类构造器说明
public Socket(String host,int port)根据指定的服务器IP、端口号请求与服务器建立连接,连接通过,就获得了客户端socket
方法说明
public OutputStream getOutputStream( )获得字节输出流对象
public InputStream getInputStream()获得字节输入流对象
public class TcpClient {
    public static void main(String[] args) throws Exception {
        //1、创建一个Socket对象,并同时请求服务器进行连接(传入要发送给服务端的IP和对应的端口号)
        Socket  socket=new Socket(InetAddress.getLocalHost().getHostAddress(),6060);
        //2、通过调用字节输出流
        OutputStream os= socket.getOutputStream();
        //3、将原始字节输出流转为数据字节输出流(性能更好)
        DataOutputStream  dos=new DataOutputStream(os);
        //4、调用写出方法
        dos.writeUTF("通过TCP协议发送信息");
        //5、关闭字节流
        dos.close();
        //6、释放连接资源
        socket.close();
    }
}

2、TCP服务端开发

服务端是通过java.net包下的ServerSocket类来实现的。

ServerSocket类构造器说明
public ServerSocket( int port )为服务端程序注册端口
方法说明
public Socket accept()阻塞等待客户端的连接请求,一旦与某个客户端连接成功,则返回服务端这边的Socket对象
//服务端
public class TcpService {
    public static void main(String[] args) throws Exception {
        //1、创建一个服务端连接对象serverSocket,并设置服务端端口
        ServerSocket  serverSocket=new ServerSocket(6060);
        //2、调用accept方法,监测是否又客户端发起连接,连接成功,返回Socket对象
        Socket  socket=serverSocket.accept();
        //3、通过Socket对象,调用字节输入流
        InputStream  is=socket.getInputStream();
        //4、为提高性能,将原始字节流转换为数据字节流
        DataInputStream dis=new DataInputStream(is);
        //5、调用读方法,读出发送过来的数据
        String  str=  dis.readUTF();
        System.out.println(str);
        //6、关闭字节流
        dis.close();
        //7、释放资源
        serverSocket.close();
    }
}
3.1 TCP客户端和服务端开发-多收多发

这个案例只能接收一个程序的信息,没法实现接收多个客户端发送的信息:因为服务端现在只有一个主线程,只能处理一个客户端的消息。

//服务端
public class TcpService {
    public static void main(String[] args) throws Exception {
        System.out.println("------服务端已启动--------");
        //1、创建一个服务端连接对象serverSocket,并设置服务端端口
        ServerSocket  serverSocket=new ServerSocket(6060);
        //2、调用accept方法,监测是否又客户端发起连接,连接成功,返回Socket对象
        Socket  socket=serverSocket.accept();
            //3、通过Socket对象,调用字节输入流
            InputStream  is=socket.getInputStream();
            //4、为提高性能,将原始字节流转换为数据字节流
            DataInputStream dis = new DataInputStream(is);
        while (true) {
            //5、调用读方法,读出发送过来的数据
            try {
                String  str=  dis.readUTF();
                System.out.println("已接收:"+str);
            } catch (Exception e) {
                //如果客户端退出,那么服务端,会出异常,这时就可以捕获异常,进行处理(获取离线的IP)
                System.out.println(socket.getRemoteSocketAddress()+":已离线");
                //释放资源
                dis.close();
                socket.close();
                break;
            }
        }
    }
}
//客户端
public class TcpClient {
    public static void main(String[] args) throws Exception {
        //1、创建一个Socket对象,并同时请求服务器进行连接(传入要发送给服务端的IP和对应的端口号)
        Socket  socket=new Socket(InetAddress.getLocalHost().getHostAddress(),6060);
        //2、通过调用字节输出流
        OutputStream os= socket.getOutputStream();
        //3、将原始字节输出流转为数据字节输出流(性能更好)
        DataOutputStream  dos=new DataOutputStream(os);
        Scanner scanner=new Scanner(System.in);
        while (true) {
            System.out.print("请说:");
            String str=scanner.nextLine();
            if(str.equals("0")){
                //5、关闭字节流
                dos.close();
                //6、释放连接资源
                socket.close();
                break;
            }
            //4、调用写出方法
            dos.writeUTF(str);
        }



    }
}
3.2 TCP客户端和服务端开发-接收多客户端信息

通主线程接接收倒Socket对象,主线程将其交给一个子线程来处理,每多一个socket就交给一个子线程。

在这里插入图片描述

//客户端
public class TcpClient {
    public static void main(String[] args) throws Exception {
        //1、创建一个Socket对象,并同时请求服务器进行连接(传入要发送给服务端的IP和对应的端口号)
        Socket  socket=new Socket(InetAddress.getLocalHost().getHostAddress(),6060);
        //2、通过调用字节输出流
        OutputStream os= socket.getOutputStream();
        //3、将原始字节输出流转为数据字节输出流(性能更好)
        DataOutputStream  dos=new DataOutputStream(os);
        Scanner scanner=new Scanner(System.in);
        while (true) {
            System.out.print("请说:");
            String str=scanner.nextLine();
            if(str.equals("0")){
                //5、关闭字节流
                dos.close();
                //6、释放连接资源
                socket.close();
                break;
            }
            //4、调用写出方法
            dos.writeUTF(str);
        }
    }
}
//服务端
public class TcpService {
    public static void main(String[] args) throws Exception {
        System.out.println("------服务端已启动--------");
        //1、创建一个服务端连接对象serverSocket,并设置服务端端口
        ServerSocket  serverSocket=new ServerSocket(6060);
        Socket  socket= null;

        while (true) {
            //2、调用accept方法,监测是否又客户端发起连接,连接成功,返回Socket对象
            socket = serverSocket.accept();
            //将Socket对象交给子线程进行处理
            new SocketServerThread(socket).start();
        }
    }
}
//线程类
public class SocketServerThread extends Thread {
private  Socket  socket;
public SocketServerThread(Socket socket){
    this.socket=socket;
}
    @Override
    public void run() {
        try {
            //调用Accent方法获取字节输入流
            InputStream is=socket.getInputStream();
            //将原始流转换为数据流
            DataInputStream dis=new DataInputStream(is);
            //可以获取到是哪个客户端发送的IP
            System.out.println(socket.getRemoteSocketAddress()+":已上线!");

            while (true) {
                try {
                    //读取客户端发送的信息(只要不下线就一直处理该客户端的信息)
                    String str= dis.readUTF();
                    System.out.println(str);
                } catch (Exception e) {
                    //下线
                    System.out.println(socket.getRemoteSocketAddress()+":一下线!");
                    //释放资源
                    dis.close();
                    socket.close();
                }
            }

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

        super.run();
    }
}
3.3 案例

实现群聊,一个客户端发送的信息,会被其它所有在线的客户端接收到。

每个客户端的socket还是交给服务端一个子线程进行处理,如果上线就将socket对象用集合保存起来,用来转发信息,如果下线就将其删除掉。

在这里插入图片描述

3.4 B/S架构

在这里插入图片描述

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

在这里插入图片描述

通过浏览器进行访问直接访问环路地址加端口(127.0.0.1:8080)

//服务端
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("------服务端启动-------");
        //1、创建一个serverSocket对象,开放服务器端口
        ServerSocket serverSocket=new ServerSocket(8080);

        //2、创建一个线程池,防止高并发
        ThreadPoolExecutor pool=new ThreadPoolExecutor(4*2,4*2,5, TimeUnit.MINUTES,
                new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            //3、通过serverSocket对象获取socket对象
            Socket socket=serverSocket.accept();
            //4、创建线程对象,并将线程对象交给线程池
            pool.execute(new ServerRunnable(socket));
        }
    }
}
//线程类
public class ServerRunnable implements Runnable {
    private Socket socket;
    public ServerRunnable(Socket socket) {
        this.socket=socket;
    }

    @Override
    public void run() {
        try {
            //1、通过传过来的socket对象获取字节输出流
            OutputStream os=socket.getOutputStream();
            //2、将原始输出流转换为输出字节流
            PrintStream ps=new PrintStream(os);
            //3、调用输出流方法,按照浏览器输出规则输出对应内容
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html");
            ps.println();   //必须换行
            ps.println("<div style='font-size:200px;color:green; text-align:center;'>Hello  world<div/>"); //输出html格式或者直接字符串
            //释放资源
            ps.close();
            socket.close();

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

    }
}

原文地址:https://blog.csdn.net/qq_57340195/article/details/142987724

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!