Java网络编程

‘# 网络编程所需要的基础知识

1、什么是计算机网络?

计算机网络系统就是利用通信设备和线路(WIFI或者网线)将地理位置不同、功能独立的多个计算机系统(我们自己的电脑)互联起来,以功能完善的网络软件实现网络中资源共享和信息传递的系统(qq、微信)。通过计算机的互联,实现计算机之间的通信,从而实现计算机系统之间的信息、软件和设备资源的共享以及协同工作等功能,其本质特征在于提供计算机之间的各类资源的高度共享,实现便捷地交流信息和交换思想

2、网络编程的目的是什么?

进行信息传递


我们学习的javaweb是网页编程、而网络编程主要针对TCP/IP通信。
而且javaweb是bs架构、网络编程是也是bs架构


3、网络通信的要素

如何实现网络的通信?

需要通信双方的地址=ip+端口号

那为什么我们有了地址就可以进行网络通信呢?

因为这个地址就是网络通信协议实现网络通信的时候所需要的

常见的协议http(超文本传输协议)、https(安全的超文本传输协议)、smtp(邮件传输协议)、ftp(文件上传协议)、tcp、udp,这些协议都遵循TCP/IP参考模型

OSI七层网络模型,是理想化的网络模型,我们实际使用的是TCP/IP四层网络模型

在这里插入图片描述
java的网络编程重点针对于传输层的TCP,UDP协议

4、IP

ip地址的分类

IPV4/IPV6
IPV4:127.0.0.1,由四个字节组成,每个字节的范围是0~255,大约有42亿,可以穷尽,很稀缺,北美30亿,亚洲只有4亿,2011年就用尽了
IPV6::2001:obb2:aaaa:0012:0000:2001:obb2:aaaa,一共128位,一共八组,每组4个数字,一共32个数,每个数字都是十六进制数,一个十六进制数可以写成4个二进制数(就是十六进制数转成二进制数)所以,32×4=128位

ip地址的ABCD类的区分
在这里插入图片描述
1.baiA类IP地址

一个A类IP地址由1字节(每个字节是8位)的网络地址和3个字节主机地址组成,网络地址的最高位必须是“0”,即第一段数字范围为1~126。每个A类地址可连接16387064台主机,Internet有126个A类地址。

2.B类IP地址

一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,即第一段数字范围为128~191。每个B类地址可连接64516台主机,Internet有16256个B类地址。

3.C类IP地址

一个C类地址是由3个字节的网络地址和1个字节的主机地址组成,网络地址的最高位必须是“110”,即第一段数字范围为192~223。每个C类地址可连接254台主机,Internet有2054512个C类地址。

5、端口

端口表示计算机上的一个程序的进程,像任务管理器中的PID就是PortID
在这里插入图片描述
我们打个比喻来更详细的理解
我们假设一栋楼就是一个ip,每一户具体的人家就是端口号,我们只有通过端口号才能在这栋楼中找到想要查找的人家,从该人家中拿资源

不同进程有不同的端口号!用来区分软件!端口被规定为0-65535个,分为TCP和UDP端口这两个协议每一个都有65536个端口。不同协议可以使用相同的端口号,相同的协议不能使用相同的端口

端口的分类

公有端口0~1023:HTTP:80、HTTPS:443、FTP:21、SSH:22、Telent:23
程序注册端口1024~49151:Tomcat:8080、MySQL:3306、Oracle:1521
动态/私有端口49152~65535:比如idea中创建一个html文件访问的时候默认使用的是63342端口

常用的有关网络的cmd命令

netstat -ano #查看所有的端口
netstat -ano|findstr "" #查找一个具体的端口
tasklist|findstr "" #查找某一个具体端口的进程

6、通信协议

TCP/IP协议簇(就是很多协议组成这个协议簇)
包含两个重要的协议:

  1. TCP:用户传输协议(类似于打电话,打通了才能交流信息,打不通则无法交流)
  2. UDP:用户数据报协议(类似于发邮件,我们有了对方的地址邮箱我们只管发,对方收不收得到不一定)

其中有两个出名的协议就是:TCP协议和IP协议(ip协议就是网络互联协议

解释TCP通信的过程以及涉及到得类都有什么

Socket类可以认为是客户端类
ServerSocket可以认为是服务器类
客户端请求服务器,然后就可以建立一个连接,在这个连接当中就包含IO对象,我们使用这个IO对象实现客户端和服务器端的数据交流
在这里插入图片描述
明确服务器端的两件事情:

  1. 多个客户端同时和服务器进行交互,服务器必须明确和哪个客户端进行的交互,ServerSocket类有一个accept方法可以获取到请求的客户端对象
  2. 多个客户端同时和服务器进行交互,就需要使用多个IO流对象,和谁交互就使用谁的IO流对象,因为ServerSocket类本身不提供IO流对象,它需要使用获取到的客户端对象来获取IO对象,简单来说就是服务器使用客户端的流和客户端交流

TCP和UDP对比

TCP:好比为打电话

  • 连接稳定
  • 三次握手,四次挥手
  • 客户端、服务端
  • 传输完成,释放连接,效率低

三次握手、四次挥手的简单说明

三次握手,最少需要三次,保证连接的稳定性
A:你愁啥?
B:瞅你咋地?
A:干一场!


四次挥手
A:我要走了了!
B:我知道你要走了了!
B:你真的要走了吗?
A:我真的要走了!

UDP好比为发邮件或者发短信

  • 不连接,不稳定
  • 发包没有明确的客户端和服务端的界限
  • 不管有没有准备好,都可以发给你
  • DDOS:洪水攻击,饱和攻击,发送足够多的包将对方的端口都堵塞住

Java中有关网络的类

InetAddress类

用于显示和获取ip的类

该类过于简单直接贴代码

/**
 * @author 14767
 */
public class Test {
    public static void main(String[] args) throws Exception{
        //获取本机地址
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);
        InetAddress localHost2 = InetAddress.getByName("localhost");
        System.out.println(localHost2);
        InetAddress localHost3 = InetAddress.getByName("127.0.0.1");
        System.out.println(localHost3);


        System.out.println("==============");

        InetAddress address = InetAddress.getByName("www.baidu.com");
        //返回此InetAddress对象的原始IP地址。
        System.out.println(Arrays.toString(localHost.getAddress()));
        //获取此IP地址的完全限定域名。
        /**
         * 这个完全限定名在访问百度的ip的时候返回的是它的ip地址
         * 但是在访问本地的ip的时候返回的是主机名
         */
        System.out.println(localHost.getCanonicalHostName());
        //获取此ip地址的主机名
        System.out.println(localHost.getHostName());
        //获取本机的对外开放的ip
        /**
         * 这里发生了很有趣的一件事
         * 由于我的vmware虚拟网卡开着的,不知道为什么干扰到我获取喔本机ip了
         * 获取到的是VMware的ip地址
         * 由于我没找到合适的解决办法只好将虚拟网卡禁用
         */
        System.out.println(localHost.getHostAddress());
    }
}

我的主机名位lalala
执行效果为
在这里插入图片描述

InetSocketAddress类

过于简单直接贴代码

public class TestInetSocketAddress {
    public static void main(String[] args) {
        InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080);

        //获取传入的参数的ip地址
        System.out.println(socketAddress.getAddress());
        //获取ip地址的主机名
        System.out.println(socketAddress.getHostName());
        //如果没有主机名(使用文字创建),则返回主机名或地址的String形式。
        System.out.println(socketAddress.getHostString());
        //获取该对象的端口号
        System.out.println(socketAddress.getPort());
    }
}

在这里插入图片描述

ServerSocket类

为了方便理解,避开套接字的概念,我们可以姑且认为该类是一个服务器类,可以使用该类创建一个服务器对象

ServerSocket(int port)构造方法

创建绑定到指定端口的服务器套接字,这时API文档中的描述,我们可以理解为就是创建一个服务器对象并且绑定端口号

Socket accept() 方法

侦听要连接到此套接字并接受它。 我们可以理解为该方法就是监听客户端的连接的,该方法类似于scanner方法,只要没有输入字符,程序就会一直运行停在那里。accept方法就同理,只要没有监听到客户端的连接就一直停在这里。该方法返回的就是连接过来的客户端对象,俗称阻塞式监听

Socket类

该类的API文档描述为该类实现客户端套接字(也称为“套接字”)。 套接字是两台机器之间通讯的端点。 套接字的实际工作由SocketImpl类的实例执行。 应用程序通过更改创建套接字实现的套接字工厂,可以配置自己创建适合本地防火墙的套接字。

同样为了开套接字的理解,我们可以理解为Socket类就是一个用于创建客户端的类

Socket(InetAddress address, int port)构造方法

该方法用于创建一个客户端对象,根据参数address也就是ip,和port一起组成为该客户端对象的要连接服务器端的地址(也就是套接字)

getInputStream() 和getOutputStream()

这两个方法用于返回此客户端的流对象,这两个流对象用于客户端和服务器端的信息传递

举例示范TCP消息传递

服务器类

public class TcpServerDemo01 {
    public static void main(String[] args) {
        ByteArrayOutputStream baos = null;
        InputStream is = null;
        Socket clientSocket = null;
        ServerSocket serverSocket = null;
        try {
            //创建绑定到指定端口的服务器套接字(也就是创建服务器端)
            serverSocket = new ServerSocket(9999);
            //循环监听是否有请求连接此服务器端
            while (true){
                //侦听要连接到此套接字的请求并接受它。
                clientSocket = serverSocket.accept();
                //读取客户端消息
                is = clientSocket.getInputStream();
                baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len;
                while ((len = is.read(buffer)) != -1) {
                    baos.write(buffer,0,len);
                }
                System.out.println(baos.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (clientSocket != null) {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端类

public class TcpClientDemo01 {
    public static void main(String[] args) {
        Socket clientSocket = null;
        OutputStream os = null;
        try {
            //首先需要知道服务器的地址和端口号
            InetAddress serverIP = InetAddress.getByName("127.0.0.1");
            //确认端口号
            int port = 9999;
            //创建套接字(可以理解为创建客户端)
            clientSocket = new Socket(serverIP, port);
            //发送消息,有了套接字就可以确定将io流流向哪里
            os = clientSocket.getOutputStream();
            os.write("你好我是张三".getBytes());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (clientSocket != null) {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

示范文件上传案例

服务器端

public class TcpServerDemo02 {
    public static void main(String[] args) throws Exception{
        //创建服务器并绑定该服务器的端口号
        ServerSocket server = new ServerSocket(9999);
        //循环监听客户端发送的输出流
        while (true){
            //获取连接过来的客户端
            Socket client = server.accept();
            //使用该客户端获取输入流,因为服务区对象没有创建流的方法
            InputStream is = client.getInputStream();
            //创建文件字节输出流用于将从客户端获取到的的输入流中的数据文件存入服务器中
            FileOutputStream fos = new FileOutputStream("pic.jpg");
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
            fos.close();
            is.close();
        }

    }
}

客户端

public class TcpClientDemo02 {
    public static void main(String[] args) throws Exception{
        //创建客户端
        Socket client = new Socket(InetAddress.getByName("127.0.0.1"),9999);
        //创建输出流,用于输出给服务器端
        OutputStream os = client.getOutputStream();
        //获取文件字节输入流用于将文件输入到内存当中
        FileInputStream fis = new FileInputStream("C:\\Users\\14767\\Desktop\\1.jpg");
        //创建缓冲字节数组
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) != -1){
            //将读取到内存中的缓冲数组中的数据放入输出流当中
            os.write(buffer,0,len);
        }
        //关闭资源
        fis.close();
        os.close();
    }
}

举例示范UDP消息传递

消息发送端

public class UdpClientDemo01 {
    public static void main(String[] args) throws Exception{
        //创建一个socket这个套接字,我们可以认为他是服务器端,也可以认为他是客户端,因为在UDP协议中没有明确的服务器端和客户端的概念
        DatagramSocket socket = new DatagramSocket();
        //建立一个包
        String msg = "你好我是张三";
        DatagramPacket packet = new DatagramPacket(
                //要传递的信息的字节数组
                msg.getBytes(),
                //要传递的字节数组从那里开始传递,从哪里结束
                0,msg.getBytes().length,
                //传递到那里去,传递过去的地址
                InetAddress.getByName("localhost"),
                9090);
        //发送包
        socket.send(packet);
        //关闭资源
        socket.close();
    }
}

消息接收端

public class UdpServerDemo01 {
    public static void main(String[] args) throws Exception {
        //创建socket并且开放端口
        DatagramSocket socket = new DatagramSocket(9090);
        byte[] b = new byte[1024];
        //创建要接收的包
        DatagramPacket packet = new DatagramPacket(b, 0, b.length);
        //接收数据包,阻塞式接收
        socket.receive(packet);
        byte[] data = packet.getData();
        System.out.println(new String(data));
        //关闭连接
        socket.close();
    }
}
posted @ 2021-02-20 16:32  谢海川  阅读(38)  评论(0)    收藏  举报