JAVA 网络编程(十九)

网络编程(十九)

网络编程:计算机与计算机之间通过网络进行数据传输。

Java中可以使用java.net包下的技术开发出常见的网络应用程序。

常见的软件架构

CS

CS,Client/Server,客户端/服务器,在用户本地需要下载并安装客户端程序,在远程有一个服务器端程序。

通过客户端访问服务器。

适合定制专业化的办公类软件,例如:IDEA,网游

优点

  1. 快速响应:由于客户端和服务端的分工协作,能够迅速响应用户请求,提供更快的响应速度。
  2. 功能复杂:客户端可以在响应服务端的同时执行一些处理逻辑,提供更加完整的功能。
  3. 易于维护:由于各模块分工明确,每个模块可以独立开发,便于管理和维护。

缺点

  1. 成本高:客户端和服务端需要独立开发和管理,所需人力物力较多。
  2. 安全性差:由于客户端和服务端之间的通信频繁,增加了被攻击的风险。

BS

BS,Brower/Server,浏览器/服务器,只需要一个浏览器,用户通过不同的网址,客户访问不同的服务器。

通过浏览器访问服务器。

适合移动互联网应用,可以在任何地方任何时间访问的系统。

优点

  1. 低成本:相比于CS架构,BS架构只需服务端部分进行专门的开发,降低了成本。
  2. 易于维护:应用逻辑全部集中在服务端,便于后台管理。
  3. 安全性高:由于所有操作都在服务器端进行,可以减少安全隐患,实现较高的防御能力。

缺点

  1. 响应速度慢:由于服务器承担了所有的计算和存储任务,文件的传输和页面的刷新等耗时的操作都需要等待服务器完成,这可能导致用户体验不佳。
  2. 功能不足:由于大多数处理逻辑都在服务器端完成,用户可能无法获得完整的用户体验。

网络编程三要素

  1. IP:设备在网络中的地址,是唯一的标识
  2. 端口号:应用程序在设备中唯一的标识
  3. 协议:数据在网络中传输的规则,常见的协议有UDP,TCP,http,https,ftp

IP

全称:Internet Protocol,是互联网协议地址,也称IP地址,是分配给上网设备的数字标签,即上网设备在网络中的地址,是唯一的。

常见IP分类:IPv4,IPv6

IPv4

全称:Internet Protocol Version 4,互联网通信协议第四版,采用32位地址长度,分为4组,每组8位二进制数。

可利用点分十进制表示法来表示。

目前的主流方案,最多只有2^32次方个IP,目前已经用完,现在利用局域网IP解决IP不够的问题。

IPv4的地址分类形式:

  • 公网地址(万维网使用)和私有地址(局域网使用)
  • 192.168.开头代表的就是私有地址,范围为192.168.0.0-192.168.255.255,专门为组织机构内部使用,以此节省IP。

特殊IP地址:127.0.0.1,也可以是localhost,是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机。

IPv6

全称:Internet Protocol Version 6,互联网通信协议第六版,采用128位地址长度,分为8组,每组16位二进制数。

可利用冒分十六进制表示法

最多有2^128次方个IP。

端口号

由两个字节表示的整数,取值范围:0-65535

其中0-1023之间的端口用于一些知名的网络服务或应用,我们自己使用1024以上的端口号就可以了。

:一个端口号只能被一个应用程序使用

协议

计算机网络中,连接和通信的规则被称为网络通信协议。

  • OSI参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未在因特网上进行广泛传播
    • 应用层
    • 表示层
    • 会话层
    • 传输层
    • 网路层
    • 数据链路层
    • 物理层
  • TCP/IP参考模型:或TCP/IP协议,事实上的国际标准
    • 应用层:HTTP,FTP,Telnet,DNS…
    • 传输层:TCP,UDP…
    • 网络互联层:IP,ICMP,ARP…
    • 网络接入层:硬件设备0101010…
UDP协议

用户数据报协议,User Datagram Protocol

UDP是面向无连接(不管是否连接成功)的通信协议。

速度快,有大小限制,一次最多发64K,数据不安全,易丢失数据。

TCP协议

传输控制协议,Transmission Control Protocol

TCP协议是面向连接(确保连接成功)的通信协议。

速度慢,无大小限制,数据安全。

InetAddress类

此类表示互联网协议(IP)的地址,该类并未对外提供构造方法,需要通过静态方法getByName获取对象。

InetAddress有两个子类:Inet4Address,Inet6Address

getByName方法会判断是使用的IPv4还是IPv6。

若是IPv4则会创建Inet4Address对象进行返回。

若是IPv6则会创建Inet6Address对象进行返回。

常用方法

getByName

格式

static InetAddress getByName(String host)

说明:确定主机名称的IP地址,主机名称可以是机器名称,也可以是IP地址。

getHostName

格式

String getHostName()

说明:获取此IP地址的主机名

getHostAddress

格式

String getHostAddress()

说明:返回文本显示中的IP地址字符串

范例
import java.net.InetAddress;
import java.net.UnknownHostException;

public class Test {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress address1=InetAddress.getByName("192.168.19.1");
        System.out.println(address1);//打印出 /IP地址
        System.out.println(address1.getHostAddress());//打印出IP地址
        System.out.println(address1.getHostName());//打印出设备名称

        System.out.println();
        InetAddress address2=InetAddress.getByName("DESKTOP-FAEF9UU");
        System.out.println(address2);//打印出 设备名/IP地址
        System.out.println(address2.getHostAddress());//打印出IP地址
        System.out.println(address2.getHostName());//打印出设备名称
    }
}

UDP通信程序

发送数据

步骤

  1. 创建发送端的DatagramSocket对象
    • 绑定端口,以后我们就是通过这个端口往外发送
    • 空参:所有可用的端口中随机一个进行使用
    • 有参:指定端口号进行使用
  2. 数据打包DatagramPacket
  3. 发送数据
  4. 释放资源

接收数据

步骤

  1. 创建接收端的DatagramSocket对象
  2. 接收打包好的数据
  3. 解析数据包
  4. 释放资源

范例1

//必须先执行接收文件,再执行发送文件
//ReceiveMessageDemo.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class ReceiveMessageDemo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket(10086);

        byte[] bytes=new byte[1024];
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length);
        ds.receive(dp);

        byte[] data=dp.getData();
        int length=dp.getLength();
        InetAddress address =dp.getAddress();
        int port=dp.getPort();
        System.out.println("接收到数据"+new String(data,0,length));
        System.out.println("该数据是从"+address+"电脑中端口为"+port+"的位置发出");

        ds.close();
    }
}
/*
接收到数据你好
该数据是从/127.0.0.1电脑中端口为52727的位置发出
*/

//SendMessageDemo.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        //1.创建DatagramSocket对象
        DatagramSocket ds=new DatagramSocket();

        //2.打包数据
        InetAddress address=InetAddress.getByName("127.0.0.1");
        String s="你好";
        byte[] bytes=s.getBytes();
        int port=10086;
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);
        //3.发送数据
        ds.send(dp);
        //4.释放数据
        ds.close();
    }
}

范例2

//按照下面的要求实现程序
//UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
//UDP接收数据:因为接收端不知道发送端什么时候停止,故采用死循环
//SendMessageDemo.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket();
        Scanner sc=new Scanner(System.in);
        while (true) {
            System.out.print("请输入你要传输的数据:");
            String s=sc.nextLine();
            byte[] bytes=s.getBytes();
            InetAddress address=InetAddress.getByName("127.0.0.1");
            int port=10086;
            DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);
            ds.send(dp);
            if(s.equals("886")){
                System.out.println("通讯结束");
                break;
            }
        }
        ds.close();
    }
}

//ReceiveMessageDemo.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class ReceiveMessageDemo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket(10086);
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        while(true){
            ds.receive(dp);
            int port = dp.getPort();
            String address = dp.getAddress().getHostAddress();
            String name = dp.getAddress().getHostName();
            byte[] data = dp.getData();
            int len = dp.getLength();
            System.out.println("接收到数据:");
            String s = new String(data, 0, len);
            System.out.println(s);
            System.out.println("该数据来自" + address + "主机名"+name+"端口为" + port + "的位置");
        }
    }
}

通信方式

  1. 单播:只给一个网络设备发送数据,之前的两个范例就是单播
  2. 组播:给一组网络设备发送数据
    • 组播地址:224.0.0.0-239.255.255.255,其中224.0.0.0-224.0.0.255为预留的组播地址
    • 组播接收端:不创建DatagramSocket对象,而需要创建MulticastSocket对象,且需要将当前本机添加到指定组中
    • 组播发送端:不创建DatagramSocket对象,而需要创建MulticastSocket对象,需将目的地址改成组播地址
  3. 广播:给局域网内所有网络设备发送数据
    • 广播地址:255.255.255.255
    • 和单播大致没区别,只需要把地址目的地址改为255.255.255.0
组播范例
//SendMessageDemo.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.Scanner;

public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        MulticastSocket ms=new MulticastSocket();
        System.out.print("请输入数据:");
        Scanner sc=new Scanner(System.in);
        String s=sc.nextLine();
        byte[] bytes=s.getBytes();
        InetAddress address=InetAddress.getByName("224.0.0.1");
        int port=10086;
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);
        ms.send(dp);
        ms.close();
    }
}

//ReceiveMessageDemo1.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class ReceiveMessageDemo1 {
    public static void main(String[] args) throws IOException {
        MulticastSocket ms=new MulticastSocket(10086);

        InetAddress address=InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

        byte[] bytes=new byte[1024];
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length);
        ms.receive(dp);

        int port=dp.getPort();
        String sendAddress=dp.getAddress().getHostAddress();
        String sendName=dp.getAddress().getHostName();
        int len=dp.getLength();
        byte[] data = dp.getData();
        String s = new String(data, 0, len);
        System.out.println(s);
        System.out.println("该数据来自" + sendAddress + "主机名"+sendName+"端口为" + port + "的位置");

        ms.close();
    }
}

//ReceiveMessageDemo2.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class ReceiveMessageDemo2 {
    public static void main(String[] args) throws IOException {
        MulticastSocket ms=new MulticastSocket(10086);

        InetAddress address=InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

        byte[] bytes=new byte[1024];
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length);
        ms.receive(dp);

        int port=dp.getPort();
        String sendAddress=dp.getAddress().getHostAddress();
        String sendName=dp.getAddress().getHostName();
        int len=dp.getLength();
        byte[] data = dp.getData();
        String s = new String(data, 0, len);
        System.out.println(s);
        System.out.println("该数据来自" + sendAddress + "主机名"+sendName+"端口为" + port + "的位置");

        ms.close();
    }
}
广播范例
//SendMessageDemo.java
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket();

        Scanner sc=new Scanner(System.in);
        System.out.print("请输入数据:");
        String s=sc.nextLine();
        byte[] bytes=s.getBytes();
        InetAddress address=InetAddress.getByName("255.255.255.255");
        int port=10086;
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);
        ds.send(dp);
        ds.close();

    }
}

//ReceiveMessageDemo1.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class ReceiveMessageDemo1 {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket(10086);
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        ds.receive(dp);
        int port = dp.getPort();
        String address = dp.getAddress().getHostAddress();
        String name = dp.getAddress().getHostName();
        byte[] data = dp.getData();
        int len = dp.getLength();
        System.out.println("接收到数据:");
        String s = new String(data, 0, len);
        System.out.println(s);
        System.out.println("该数据来自" + address + "主机名"+name+"端口为" + port + "的位置");
    }
}

TCP通信程序

TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象。

通信之前要保证连接已经建立。

通过Socket产生IO流来进行网络通信。

三次握手

  1. 客户端向服务器发出连接请求,等待服务器确认
  2. 服务器向客户端返回一个响应,告诉客户端收到了请求
  3. 客户端向服务器再次发出确认信息,连接建立

四次挥手

  1. 客户端向服务器发出取消连接请求
  2. 服务器向客户端返回一个响应,表示收到客户端取消请求,随后服务器将最后的数据处理完毕。
  3. 服务器向客户端发送确认取消信息
  4. 客户端再次发送确认信息,连接取消

代码思路

客户端

  1. 创建客户的Socket对象(Socket)与指定服务端连接,三次握手协议保证连接建立,若连接不上,代码会报错
    • Socket(String host,int port)
  2. 获取输出流,写数据
    • OutputStream getOutputStream()
  3. 释放资源,利用四次挥手协议断开连接,且确保连接通道里的数据已经处理完毕
    • void close()

服务器

  1. 创建服务端的Socket对象(ServerSocket)
    • ServerSocket(int port)
  2. 监听客户单连接,返回一个Socket对象
    • Socket accept()
  3. 获取输入流,读数据,并把数据显示在控制台
    • InputStream getInputStream
  4. 释放资源
    • void close()

范例

//需要先运行服务端
//client.java
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("127.0.0.1",10086);
        String s="hello!你好!";
        OutputStream os=socket.getOutputStream();
        os.write(s.getBytes());
        os.close();
        socket.close();
    }
}

//Server.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10086);
        Socket accept = ss.accept();
        BufferedReader br=new BufferedReader(new InputStreamReader(accept.getInputStream()));
        int ch;
        while((ch=br.read())!=-1){
            System.out.print((char)ch);
        }
        br.close();
        accept.close();
        ss.close();
    }
}

范例

例1

//多发多收
//客户端:多次发送数据
//服务器多次接收数据,并打印
//Client.java
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("127.0.0.1",10086);
        Scanner sc=new Scanner(System.in);
        OutputStream os=socket.getOutputStream();
        while (true) {
            System.out.print("请输入数据:");
            String s=sc.nextLine();
            if(s.equals("886")){
                break;
            }
            os.write(s.getBytes());
        }
        socket.close();
    }
}
//Server.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10086);
        Socket socket=ss.accept();
        BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        int ch;
        while((ch=br.read())!=-1){
            System.out.print((char)ch);
        }
        socket.close();
        ss.close();
    }
}

/*client的运行
请输入数据:saf
请输入数据:453
请输入数据:996
请输入数据:886
*/
/*Server的运行
saf453996
*/

例2

//接收与反馈
//客户端:发送一条消息,接收服务端反馈的消息并打印
//服务器:接收数据并打印,在给客户端反馈消息

//Server.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        //1.创建对象并绑定10086接口
        ServerSocket ss=new ServerSocket(10086);
        //2.等待客户端连接
        Socket s=ss.accept();
        //3.socket中获取输入流读取数据
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        int ch;
        //细节:read方法会从接连通道中读取数据
        //需要有一个结束标记,此处循环才会停止
        //程序就会一直挺爱read方法这里,等待读取下面的数据
        while ((ch=br.read())!=-1){
            System.out.print((char)ch);
        }
        //4.回写数据
        OutputStream os = s.getOutputStream();
        os.write("服务端已收到消息".getBytes());
        //5.释放资源
        s.close();
        ss.close();
    }
}
//Client.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建socket对象连接客户端
        Socket s=new Socket("127.0.0.1",10086);
        //2.写出数据
        OutputStream os = s.getOutputStream();
        os.write("你好".getBytes());
        //3.写出一个结束标记
        s.shutdownOutput();

        //4.接收服务端回写的数据
        BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
        int ch;
        while ((ch=br.read())!=-1){
            System.out.print((char)ch);
        }
        //5.释放资源
        os.close();
    }
}
/*Server收到的消息
你好
*/
/*Client发送的消息
服务端已收到消息
*/

例3

//上传文件
//客户端:将本地文件上传到服务器,接收服务器的反馈
//服务器:接收客户端上传的文件,上传完毕后给出反馈
//服务器接收后文件是唯一的,每次接收的内容一样,文件名不一样
//利用UUID类的randomUUID方法实现,UUID表示通用唯一标识符UUID类,UUID表示一个128位的值)
//Server.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10086);
        Socket accept = ss.accept();
        BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
        String name= UUID.randomUUID().toString().replace("-","");
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("serverphoto\\"+name+".jpg"));
        byte[] bytes=new byte[1024];
        int len;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write("上传成功");
        bw.newLine();
        bw.close();
        bos.close();
        accept.close();
        ss.close();
    }
}
//Client.java
import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket s=new Socket("127.0.0.1",10086);
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("clientphoto\\photo.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        byte[] bytes=new byte[1024];
        int len;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        bos.flush();
        s.shutdownOutput();
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String str=br.readLine();
        System.out.println(str);
        br.close();
        bis.close();
        s.close();
    }
}

例4

//上传文件(多线程版)
//若服务器不停止,能接收很多用户上传的图片
//提示:可用循环或多线程,但循环不合理,最优解法(循环+多线程+线程池)改写
//Server.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Server {
    public static void main(String[] args) throws IOException {
        ThreadPoolExecutor pool=new ThreadPoolExecutor(
                3,
                16,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        ServerSocket ss=new ServerSocket(10086);
        while (true) {
            Socket accept=ss.accept();
            pool.execute(new MyRunnable(accept));
        }
    }
}
//Client.java
import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket s=new Socket("127.0.0.1",10086);
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("clientphoto\\photo.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        byte[] bytes=new byte[1024];
        int len;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        bos.flush();
        s.shutdownOutput();
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String str=br.readLine();
        System.out.println(str);
        br.close();
        bis.close();
        s.close();
    }
}

例5

//BS,接收浏览器的消息并打印
//无需写客户端
//服务器:接收数据并打印
//在浏览器搜索 127.0.0.1:端口号
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10086);
        Socket accept = ss.accept();
        InputStreamReader isr = new InputStreamReader(accept.getInputStream());

        int ch;
        while ((ch=isr.read())!=-1){
            System.out.print((char)ch);
        }
        accept.close();
        ss.close();

    }
}
  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值