14Java进阶网络编程API

1.网络协议的三要素:语义、语法和时序

语义表示要做什么,语法表示要怎么做,时序表示做的顺序。

2.网络OSI七层模型

OSI/RM 模型(Open System Interconnection/Reference Model)。它将计算机网络体系结构的通信协议划分为七层,自下而上依次为物理层(Physics Layer)、数据链路层(Data Link Layer)、网络层(Network Layer)、传输层(Transport Layer)、会话层(Session Layer)、表示层(Presentation Layer)、应用层(Application Layer)。

3.TCP四层模型简介

自下而上依次为网络接口层(Network Interface Layer)、网络层(Network Layer)、传输层(Transport Layer)和应用层(Application Layer)。

还可以转换为五层模型,即将网络接口层分为物理层和数据链路层。

3.1TCP四层作用

物理层规定了物理介质的各种特性,包括机械特性、电子特性、功能特性和规程特性。

数据链路层负责接收数据帧并通过网络发送,或从网络上接收物理帧再抽离出数据帧交给网络层。

网络层管理网络中的数据通信,设法将数据从源端经过若干中间节点传送到目的端,从而向传输层提供最基本的端到端的数据传送服务。

传输层主要功能包括分割和重组数据并提供差错控制和流量控制,以达到提供可靠传输的目的。为了实现可靠的传输,传输层协议规定接收端必须发送确认信息以确定数据达到,假如数据丢失,必须重新发送。

应用层对应于 OSI 七层模型的会话层、表示层和应用层,该层向用户提供一组常用的应用程序服务。

3.2传输层的常用协议

1.传输控制协议(Transmission Control Protocol,TCP),是一种可靠的面向连接的传输服务协议。在 TCP/IP 协议族中,TCP 提供可靠的连接服务,采用“三次握手”建立一个连接。

2.用户数据报协议(User Datagram Protocol,UDP),是另外一个重要的协议,它提供的是无连接、面向事务的简单不可靠信息传送服务。UDP 不提供分割、重组数据和对数据进行排序的功能,也就是说,当数据发送之后,无法得知其是否能安全完整地到达。

UDP是无连接的、不可靠的,资源消耗小,处理速度快,但是在网络不好的情况下丢包比较严重。

3.3应用层的常用协议

  1. 文件传输协议(File Transfer Protocol,FTP),上传、下载文件可以使用 FTP 服务。

  2. Telnet 协议,提供用户远程登录的服务,使用明码传送,保密性差,但简单方便。

  3. 域名解析服务(Domain Name Service,DNS),提供域名和 IP 地址之间的解析转换。

  4. 简单邮件传输协议(Simple Mail Transfer Protocol,SMTP),用来控制邮件的发送、中转。

  5. 超文本传输协议(Hypertext Transfer Protocol,HTTP),用于实现互联网中的 WWW 服务。

  6. 邮局协议的第三个版本(Post Office Protocol 3,POP3),它是规定个人计算机如何连接到互联网上的邮件服务器进行收发邮件的协议。

3.4数据的封装与解封

从上向下,数据的传输需要加上相应的头部和尾部,称为数据的封装。

从下向上,数据的传输需要去掉相应的头部和尾部,称为数据的解封。

4 IP地址及其表示

IP地址由两部分组成:网络号和主机号

网络号表示该地址处于哪一个网络,主机号表示该地址的主机。

IP 地址有两种表示方式,二进制表示和点分十进制表示,常见的是点分十进制表示的 IP 地址。IP 地址的长度为 32 位,每 8 位组成一个部分,一个 IP 地址可以分为四个部分。如果每个部分用十进制表示,其值的范围为 0 ~ 255,不同部分之间用“.”分割开来。

5 域名简介及其分类

域名可分为不同级别,包括顶级域名、二级域名等。

顶级域名又可分为以下两类:

一类是国家顶级域名,200 多个国家都按照 ISO3166 国家代码分配了顶级域名,例如中国是 cn,美国是 us,韩国是 kr 等。

另一类是国际顶级域名,一般表示着注册企业类别的符号,例如表示工商企业的 com,表示网络提供商的 net,表示非营利组织的 org 等。

二级域名是指顶级域名之下的域名,例如在国际顶级域名下,由域名注册人申请注册的网上名称,例如 sohu、apple、microsoft 等。

6 InetAddress——获取IP地址

  • InetAddress[] getAllByName(String host):通过主机名和配置名返回IP地址。

  • InetAddress getByAddress(byte[] addr):通过 IP 地址数组返回 InetAddress 对象。

  • InetAddress getByAddress(String host, byte[] addr):根据提供的主机名 host 和字节数组形式的 IP 地址 addr,创建 InetAddress 对象。

  • InetAddress getByName(String host):给定主机名 host,返回 InetAddress 对象。

  • InetAddress getLocalHost():返回本地主机 InetAddress 对象。

InetAddress 类的其他常用方法有以下几种:

  • byte[] getAddress():返回此 InetAddress 对象的 IP 地址数组。

  • String getCanonicalHostName():返回此 IP 地址的完全限定域名。完全限定域名是指主机名加上全路径,全路径中列出了序列中所有域成员。

  • String getHostAddress():返回 IP 地址字符串。

  • String getHostName():返回此 IP 地址的主机名。

7 URL类——获取网络资源的位置

构造方法:URL(地址)

获取页面的输入字节流:url.openStream()

使用IO方法对页面的内容进行获取和解析

8 URLConnection类——连接通信

1.使用url.openConnection()获取连接对象。

2.设置参数和一般请求属性。

3.使用connect()方法进行远程连接。

8.1 URLConnection的具体属性

  • boolean doInput :将 doInput 标志设置为 true,指示应用程序要从 URL 连接读取数据,此属性的默认值为 true。此属性由 setDoInput() 方法设置,其值由 getDoInput() 方法返回。

  • boolean doOutput :将 doOutput 标志设置为 true,指示应用程序要将数据写入 URL 连接,此属性的默认值为 false。此属性由 setDoOutput() 方法设置,其值由 getDoOutput() 方法返回。

  • boolean useCaches :如果其值为 true,则只要有条件就允许协议使用缓存;如果其值为 false,则该协议始终必须获得此对象的新副本,其默认值为上一次调用 setDefaultUseCaches() 方法时给定的值。此属性由 setUseCaches() 方法设置,其值由 getUseCaches() 方法返回。

  • boolean connected:表示URL是否连接成功

  • URL url:表示Connection类在网上打开的url对象。

9 使用Socket编程之TCP Socket

Socket:套接字,用于端到端的通信。

ServerSocket:用于服务端对象的创建。服务器会初始化一个端口号的Socket,监听此端口的连接。如果客户端建立连接,会分配一个带有新的端口号的Socket,用来和客户端对话。当连接结束时,会关闭Socket。

ServerSocket的accept()方法:ServerSocket的accept()方法从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的Socket对象,并将它返回。如果队列中没有连接请求,accept()方法就会一直等待,直到接收到了连接请求才返回。

服务器为请求连接的客户进程建立一个先进先出队列,默认大小一般是50,如果调用accept()方法就会从队列中取出连接请求,服务端为其建立一个socket。如果队列已经满,服务器会拒绝新的连接请求。

9.1使用Socket创建CS连接

package two;
​
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
​
public class Test7 {
​
}
class TestServer{
    public static void main(String[] args) {
        ServerSocket socket = null;
        try {
            //创建服务端的socket
            socket = new ServerSocket(8888);
            //创建一个socket,接受客户端的套接字
            Socket s = socket.accept();
            //显示
            System.out.println("客户端的IP:"+s.getInetAddress()+",客户端的端口号:"+s.getPort());
            s.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
class TestClient{
    public static void main(String[] args) {
        try {
            //服务端和客户端在同一局域网内。服务端的端口号是8888
            Socket socket = new Socket("127.0.0.1",8888);
            //从socket中获得客户端的端口号和IP
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            if(dis.available()!=0)System.out.println(dis.readUTF());
            dis.close();
            socket.close();
        }catch (ConnectException e) {
            e.printStackTrace();
            System.err.println("服务器连接失败!");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
​
    }
}

 

9.2使用socket进行图片上传

package org.lanqiao.service;
​
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
​
public class UploadService {
    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(10203);
            Socket socket = server.accept();
            //获取对客户端写入的数据输出字节流
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            File file = new File("timg.jpg");
            //从本地图片获得输入流
            FileInputStream fis = new FileInputStream(file);
            //写入输出流
            dos.write(fis.readAllBytes());
            dos.close();
            fis.close();
            socket.close();
            server.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
package org.lanqiao.client;
​
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
​
public class UploadClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 10203);
            //获取服务端发来的输入流
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            File file = new File("pic/mn.jpg");
            FileOutputStream fos = new FileOutputStream(file);
            //将图片数据写入本地文件
            fos.write(dis.readAllBytes());
            System.out.println("上传完成");
            fos.close();
            dis.close();
            socket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
}

 

9.3 使用多线程优化CS聊天室

package two;
​
import jdk.net.Sockets;
​
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
​
public class Test8 {
    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(8888);
            while (true) {
                //只要服务端开启,就一直接受客户端的socket
                //ServerSocket的accept()方法从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的Socket对象,并将它返回。如果队列中没有连接请求,accept()方法就会一直等待,直到接收到了连接请求才返回。
                //socket中存储的是客户端的ip地址和向客户端进行通信的端口号
                Socket socket = server.accept();
                //并为这个socket启动新的服务端线程
                ServerThread st = new ServerThread(socket);
                st.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
​
class ServerThread extends Thread {
    Socket socket;
    Scanner sc = new Scanner(System.in);
​
    public ServerThread(Socket socket) {
        this.socket = socket;
    }
​
    @Override
    public void run() {
        InputStream is = null;
        OutputStream os = null;
        try {
            //获取这个socket的输入输出流
            is = socket.getInputStream();
            os = socket.getOutputStream();
            DataInputStream dis = new DataInputStream(is);
            DataOutputStream dos = new DataOutputStream(os);
            String str = null;
            while (true) {
                //一直通信,直到客户端断开连接抛出异常
                if ((str = dis.readUTF()) != null) {
                    if (str.equals("e")) break;
                    System.out.println("客户端发来的消息:" + str);
​
                }
                System.out.println("请输入要向客户端发送的消息:");
                String msg = sc.next();
                dos.writeUTF(msg);
                System.out.println();
            }
            dos.close();
            dis.close();
            socket.close();
            //EOFException表示意外到达流的结尾,如流中断
        } catch (EOFException e) {
            System.out.println("客户端" + socket.getInetAddress().getHostAddress() + ":" + socket.getPort() + "退出!");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
​
class ClientThread extends Thread {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
​
        try {
            //建立连接,获取socket
            //socket中存储的是服务端的ip地址和向服务端进行通信的端口号
            Socket socket = new Socket("127.0.0.1", 8888);
            InputStream is = socket.getInputStream();
            OutputStream os = socket.getOutputStream();
            DataInputStream dis = new DataInputStream(is);
            DataOutputStream dos = new DataOutputStream(os);
            while (true) {
                //一直通信,直到用户输入退出连接
                if (dis.available() != 0) System.out.println("服务器发来消息:" + dis.readUTF());
                System.out.println("请输入要向服务器发送的消息(发送e结束):");
                String msg = sc.next();
                if (msg.equals("e")) {
                    System.out.println("已退出聊天");
                    break;
                }
                dos.writeUTF(msg);
            }
            dos.close();
            dis.close();
            os.close();
            is.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

10使用Socket编程之UDP Socket

DatagramSocket类:接受和发送数据报的套接字。使用UDP广播发送

DatagramPacket类:此类表示数据报包,它用来实现无连接包投递服务。根据该包中包含的地址和端口等信息,将报文从一台机器路由到另一台机器。

DatagramPacket的构造函数:

DatagramPacket(byte[] buf,int readLength):用于构建接受信息的数据报包

DatagramPacket(byte[] buf,int readLength,InetAddress inet):用于构建发送信息的数据报包,inet对象中需要指定IP和端口号

10.1使用UDP实现客户端向服务端发送消息

解决中文输入输出乱码:在发送端创建数据报包时,不要直接使用字符串或者字符串.getBytes()获得的字节数组作为数据。创建ByteArrayOutputStream和以baos为输出流的DataOutputStream,使用dos的writeUTF()方法,再获得baos使用的字节数组。将这个数组作为参数。

在接收端创建接受数据包时,不要直接显示buf创建的字符串,先创建以buf为输入的ByteArrayInputStream和以bais为输入流的DataInputStream,最后使用dis的readUTF()读出这个字节数组。这样就会识别中文、韩文等语言。

package two;
​
import java.io.*;
import java.net.*;
import java.util.Scanner;
​
public class Test10 {
}
​
class UDPClient {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        try {
            DatagramSocket ds = new DatagramSocket(9999);
            System.out.println("客户端:");
            while (true) {
                String line = sc.next();
                if (line.equals("bye")) break;
                //创建baos和dos将读入数据输出到字节数组中
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(baos);
                dos.writeUTF(line);
                byte[] buf = baos.toByteArray();
                DatagramPacket packet = new DatagramPacket(buf,buf.length, new  InetSocketAddress("127.0.0.1", 8888));
                //datagramsocket.send(datagrampacket)方法发送数据报
                ds.send(packet);
            }
            ds.close();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
​
class UDPServer {
    public static void main(String[] args) {
        try {
            //接收端socket注册端口号
            DatagramSocket ds = new DatagramSocket(8888);
            System.out.println("服务器端:");
            while (true) {
                byte[] buf = new byte[1024];
                //获取接收端数据报
                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                //datagramsocket.receive(datagrampacket)方法接受数据报
                ds.receive(packet);
                //packet.getData()获取数据信息,返回的是字节数组,需要根据数组的长度构建字符串            //使用bais和dis读取获得的字节数组。
                ByteArrayInputStream bais = new ByteArrayInputStream(buf);
                DataInputStream dis = new DataInputStream(bais);
                System.out.println(dis.readUTF());
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}

 


10.2使用UDP完成文件上传

package org.lanqiao.client;
​
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
​
public class Client {
    public static void main(String[] args) {
       File file = new File("text.txt");
        try {
            //读取文件信息到字节数组中
            DataInputStream dis = new DataInputStream(new FileInputStream(file));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            System.out.println("客户端在10203监听");
            dos.write(dis.readAllBytes());
            byte[] b = baos.toByteArray();
            dos.close();
            //创建UDPSocket和数据报包,发送字节数组  
            DatagramSocket ds = new DatagramSocket(8888);
            DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("127.0.0.1",10203));
            //发送数据报
            ds.send(dp);
            System.out.println("文件发送完成");
            ds.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
package org.lanqiao.service;
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
​
public class Service {
    public static void main(String[] args) {
        try {
            //创建Socket,注册为10203端口
            DatagramSocket ds = new DatagramSocket(10203);
            System.out.println("在10203端口监听...");
            byte[] buf = new byte[10000];
            //创建接受数据报包
            DatagramPacket packet = new DatagramPacket(buf,buf.length);
            ds.receive(packet);
           //数据输入字节流读取数据报中的数据
            ByteArrayInputStream bais = new ByteArrayInputStream(buf);
            DataInputStream dis = new DataInputStream(bais);
            File file = new File("data/text.txt");
            file.createNewFile();
             //通过字节流写入文件
            DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
            dos.write(dis.readAllBytes());
            System.out.println("文件接收完成");
            dos.close();
            dis.close();
            bais.close();
            ds.close();
        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}

 

11网络爬虫

网络爬虫:百度、谷歌等引擎从互联网中爬取数据的方式。

网络爬虫的注意事项:

  1. 不要大规模的爬取网络数据,或对对方的服务器造成较大影响。

  2. 不要将爬虫用于灰色产业、敏感行业,或使用爬虫非法获利。

  3. 不要使用爬虫获取网站或用户的隐私数据。

  4. 不要违背 robots 协议或经营者意志。

  5. 不要使用爬虫进行任何法律、法规或道德禁止的行为。

实现爬虫:使用URLConnection和给定URL建立连接,获取连接的输入流,就是网站所在的网络资源。使用正则表达式对html标签进行提取,就可以获得网页上显示的信息。

11.2 使用爬虫爬取蓝桥主页上指定标签的元素内容

package two;
​
import org.w3c.dom.ls.LSOutput;
​
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
​
public class Test14 {
    public static void main(String[] args) {
        String html = getOCode();
      //  System.out.println(html);
        String res = parseResource(html);
        System.out.println(res==null?"爬取失败":res);
    }
​
    public static String getOCode() {
        BufferedReader reader = null;
        StringBuffer html = new StringBuffer();
        try {
            URL url = new URL("https://www.lanqiao.cn/");
            URLConnection connection = url.openConnection();
            connection.connect();
            //开启连接后,创建字符输入流读取页面源代码
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                //保存到StringBuffer中
                html.append(line);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return html.toString();
    }
​
    public static String parseResource(String html) {
        String res = null;
        //Pattern.compile()创建正则表达式对象
        Pattern pattern = Pattern.compile("meta data-n-head=\"ssr\" data-hid=\"description\" name=\"description\" content=\"(.+?)\"");
        //通过pattern.matcher(源字符串)获得匹配的Matcher对象
        Matcher matcher = pattern.matcher(html);
        //如果匹配的字符串存在
        if(matcher.find()){
           //matcher.group(0)获取匹配的整个串
            res = matcher.group(0);
            //获取所需要的子串部分
            res = res.substring(res.indexOf("content=")+"content=".length());
        }
        return res;
    }
}

 


爬取结果:
group[0] :meta data-n-head="ssr" data-hid="description" name="description" content="蓝桥云课是国内领先的IT在线编程及在线实训学习平台,专业导师提供精选的实践项目,        创新的技术使得学习者无需配置繁琐的本地环境,随时在线流畅使用。以就业为导向,        提供编程、运维、测试、云计算、大数据、数据库等全面的IT技术动手实践环境,        提供Linux、Python、Java、C语言、Node.js、Hadoop、PHP、Docker、Git、        R、SQL、MongoDB、Redis、Swift、Spark等千门热门课程。"
​
​
"蓝桥云课是国内领先的IT在线编程及在线实训学习平台,专业导师提供精选的实践项目,        创新的技术使得学习者无需配置繁琐的本地环境,随时在线流畅使用。以就业为导向,        提供编程、运维、测试、云计算、大数据、数据库等全面的IT技术动手实践环境,        提供Linux、Python、Java、C语言、Node.js、Hadoop、PHP、Docker、Git、        R、SQL、MongoDB、Redis、Swift、Spark等千门热门课程。"

 

posted @ 2021-07-30 20:24  岛boki  阅读(138)  评论(0编辑  收藏  举报