网络编程

Posted AntarcticPenguin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程相关的知识,希望对你有一定的参考价值。

Java最大的特性是跨平台性,它的优势在于网络编程Java最大的特性是跨平台性,它的优势在于网络编程

一、网络编程简介

  1. C/S结构
  • 无连接用户的数据报编程
  • 面向连接的Socket编程
    • 访问数据库JDBC
    • 访问命名目录服务JNDI
    • 访问邮件服务器JavaMail
    • 访问其他服务……
  1. B/S结构
  • 服务器端JSP+Servlet
  • 客户端HTTP访问先关类

二、使用URLConnection访问Web应用

访问Web应用的,Java中提供的类包括:

  • URL,表示要访问的网页地址;
  • URLConnection,表示客户端与Web应用之间的链接,建立链接之后就可以读取从来自服务器的数据。
    下载百度首页:
public class URLConnectionDemo {
    public static void main(String[] args) {
        String urlStr = "http://www.baidu.com";
        try {
            // 创建URL对象表示百度首页地址
            URL url = new URL(urlStr);
            // 打开网站链接
            URLConnection con = url.openConnection();
            // 创建输入流对象
            BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));//这里面是进行转换的,字节流getInputStream——》字符流InputStreamReader——》缓冲流BufferedReader,从而提高读写的效率
            // 创建输出流对象
            BufferedWriter writer = new BufferedWriter(new FileWriter(
                    "D:\\baidu.html"));//相当于创建了一个文件,来保存下载的信息
            String s;
            while ((s = reader.readLine()) != null) {
                writer.write(s + "\n");
            }//这个就是从网站里面一行一行下载信息
            writer.flush();
            writer.close();
            reader.close();
        } catch (Exception e) {
        }
    }
}

这样下载的就是一个简单的网页,而没有其他的,比如说图片资源。如果想要下载整个网站,就要进一步编写代码,在下载好之后的页面分析里面的图片资源和一些超链,如果含有的话就继续下载,类似于网络爬虫。这样就可以把整个网站给爬出来了。不过这里面还要注意搜索的超链一般是两到三层,不要太多,这个可以自己设定。这样下载的就是一个简单的网页,而没有其他的,比如说图片资源。如果想要下载整个网站,就要进一步编写代码,在下载好之后的页面分析里面的图片资源和一些超链,如果含有的话就继续下载,类似于网络爬虫。这样就可以把整个网站给爬出来了。不过这里面还要注意搜索的超链一般是两到三层,不要太多,这个可以自己设定。

三、实例:提取网页中感兴趣的内容

举个例子:从当当网查找某一本书的价格,按照书号查询。在现实世界中,有很多实际的应用。比如比价网,对于同一件商品可以道不同的网站上提取价格信息进行比较。另外一个典型的例子,“去哪儿网”能够从多家航空公司网站查找机票。
其中说明了一些网站去爬别的网站的商品价格,就比如说,一家书店网站,同时监控京东上面的书的价格,只要京东上面调节了价格,这个网站同时也同步跟上。而京东也做了相应的措施,是将书的价格做成了一张图片,这样防止别的网站爬其中的数据,可是这并不能避免,因为加上面那个折扣在哪里,别的网站还是可以爬到相关的数据。贼几把厉害。这样有些网站没办法,因为人家要爬我的数据,访问我的数据,服务器在哪里也受不了啊,所以一些网站会做一个日志,提供人家去爬你的信息,给你一些权限,希望减轻服务器的压力。
下面例子,提取当当网书的价格:

public class PriceCompareDemo {
    public static void main(String[] args) {
        //这个要自己分析人家的网站,价格前后的特征标签,分析出来你要的信息在什么地方
        ServerInfo dangdang = new ServerInfo("http://search.dangdang.com/?key=","<span class=\"price_n\">&yen;","</span>");
        String isbn = "9787560619330";//9787302191773";
        double price = dangdang.find(isbn);
        System.out.printf("当当的价格是:%8.2f",price);        
    }
}
class ServerInfo{
    protected String url = null;
    String prefix = null;
    String end = null;
    public ServerInfo(String url,String prefix,String end){
        this.url = url;
        this.prefix = prefix;
        this.end = end;
    }
    private String getResult(String key){
        return url+key;
    }

    public double find(String key){
        URL url = null;
        URLConnection con = null;
        BufferedReader reader = null;
        try {
            // 创建URL对象表示要访问的网站地址
            url = new URL(getResult(key));
            // 打开网站链接
            con = url.openConnection();

            InputStream inputStream = con.getInputStream();
            // 创建输入流对象
            reader = new BufferedReader(new InputStreamReader(inputStream));
            String s;
            while ((s = reader.readLine()) != null) {    
                if(s.indexOf(prefix)>=0){
                    return getPrice(s);
                }
            }
            return -1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }finally{
            try {
                reader.close();
            } catch (IOException e) {
            }
        }
    }
    private double getPrice(String info){
        int index = info.indexOf(prefix);
        int index2 = info.indexOf(end,index);
        String price = info.substring(index+prefix.length(),index2);
        try{
            return Double.parseDouble(price);
        }catch(Exception e){
            return -1;
        }
    }
}

四、Socket通信

  • 通信包括两个程序:客户端程序和服务器端。
  • 服务器端程序工作过程:
    • 创建ServerSocket对象,指出端口。
    • 调用ServerSocket对象的accept()方法监听客户端请求,返回一个Socket对象。
    • 通过Socket对象得到输入流对象,通过Socket对象得到输出流对象。
    • 调用Socket对象的close()方法关闭连接,调用ServerSocket对象的close()方法关闭连接。
  • 客户端程序工作过程:
    • 创建Socket对象,在创建的时候需要指出服务器端的主机和端口,主机是运行服务器端程序的电脑的IP地址或者域名,端口是服务器端ServerSocket所监听的端口。
    • 调用Socket对象的connect方法建立与服务器端的连接。
    • 通过Socket对象得到输入流对象,接收来自服务器端的信息。通过Socket对象得到输出流对象,通过输出流对象向服务器端发送信息。
    • 调用Socket对象的close方法关闭连接

例子:
服务器端:Server.java

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);//创建一个服务器绑定到8888端口上去,必须是未被占用的。
        Socket s=null;
        BufferedReader in=null;
        PrintWriter out=null;
        try {
            s = ss.accept();//接收客户端的请求,只要有请求就会创建一个Socket对象。有了这个对象就可进行下面的获取输入输出流对象
            System.out.println("有客户端请求连接,客户端ip地址:"
                    + s.getInetAddress().getHostAddress() + ",远程端口:"
                    + s.getPort() + ",本地端口:" + s.getLocalPort());
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));
            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true);
            while (true) {
                String str = in.readLine();// 读取一行,接收客户端的一句话
                if (str.equalsIgnoreCase("bye"))
                    break;//判断是否为bye(不区分大小写),结束与客户端的通话
                System.out.println("接收的客户端数据:" + str);
                out.println("服务器响应:" + str);//发出一条消息
            }
            System.out.println("服务器退出");
        } finally {
            if(out!=null)
                out.close();
            if(in!=null)
                in.close();
            if(s!=null)
                s.close();
            if(ss!=null)
                ss.close();            
        }
    }
}

客户端:Client.java

public class Client {
    public static void main(String[] args) throws IOException {
        Socket s = new Socket("127.0.0.1", 8888);//找到服务器的地址、端口
        BufferedReader in = null;
        PrintWriter out = null;
        try {
             //得到输入输出流
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));
            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true);
            while (true) {
                // 从键盘接收数据
                BufferedReader inkey = new BufferedReader(new InputStreamReader(System.in));
                System.out.print("从键盘输入数据:");
                String str = inkey.readLine();// 读取一行
                out.println(str);
                if (str.equalsIgnoreCase("bye"))
                    break;
                String msg = in.readLine();
                System.out.println("从服务器收到数据:" + msg);
            }
            System.out.println("客户端退出");
        } finally {
            if(out!=null)
                out.close();
            if(in!=null)
                in.close();
            if(s!=null)
                s.close();
        }
    }
}

首先运行Server.java,让其处于监听状态。然后运行Client.java,在里面输入数据,回车就可以得到服务器返回的数据。同时打开服务器运行窗口会看到客户端的请求。直到客户端输入bye,然后客户端与服务器才进行结束。
想下:如果是远程,只要获取端口就行,就可以远程访问数据了。自己可以试一下,在服务器上创建并运行服务器Server.java,在pc上创建客户端Client.java,然后进行访问。首先运行Server.java,让其处于监听状态。然后运行Client.java,在里面输入数据,回车就可以得到服务器返回的数据。同时打开服务器运行窗口会看到客户端的请求。直到客户端输入bye,然后客户端与服务器才进行结束。

五、聊天室

  • 服务器端负责消息的转发,当客户端连接的时候,服务器把这个消息发给聊天室中所有成员,当客户端发送消息的时候,把这个消息也转发给所有成员。
  • 服务器端的程序采用多线程的方式:一个线程监听客户的请求,一个线程负责向所有客户端发送消息,对应每个线程的接收客户端信息的线程
  • 客户端程序采用多线程的方式:一个线程负责从键盘接收数据向服务器发送,另外一个线程负责从服务器端接收数据。
    服务器端:Server.java
public class Server extends Thread {
    ServerSocket ss = null;
    // 表示客户端
    private List<Socket> clients;//由于有很多客户端,所以这里创建了一个链表
    // 发送线程
    SenderThread sendThread;
    private MessageManager messageManager;//消息管理器,用来管理消息,(最下面那个类)

    public Server() {
        clients = new ArrayList<>();
        messageManager = new MessageManager(clients);
        sendThread = new SenderThread(messageManager);
        sendThread.start();
    }

    public void run() {
        try {
            ss = new ServerSocket(8888);
        } catch (Exception e) {
            System.out.println("端口被占用!");
            return;
        }
        Socket s = null;
        BufferedReader in = null;
        while (true) {
            try {
                s = ss.accept();
                clients.add(s);
                ReaderThread readerThread = new ReaderThread(s, messageManager,
                        clients);
                readerThread.start();
            } catch (Exception e) {
                e.printStackTrace();
            } 
        }
    }

    public void stopServer() {
        messageManager.addMessage("服务器要关闭了!");
        notifyAll();
        try{
            ss.close();
        }catch(Exception e){}
        for (Socket s : clients) {
            try {
                s.close();
            } catch (Exception e) {
            }
        }
    }

    public static void main(String[] args) throws IOException {
        Server server = new Server();
        server.start();
        Scanner in = new Scanner(System.in);
        while (true) {
            String str = in.nextLine();
            if (str.equals("exit")) {
                in.close();
                server.stopServer();
                System.exit(0);
            } else {
                System.out.println("无效指令!");
            }
        }
    }
}

class SenderThread extends Thread {
    private MessageManager messageManager;

    public SenderThread(MessageManager messageManager) {
        this.messageManager = messageManager;
    }

    public void run() {
        messageManager.sendMessage();
    }    
}

class ReaderThread extends Thread {
    int count=0;
    MessageManager messageManager;
    Socket s;
    String name;
    private List<Socket> clients;

    ReaderThread(Socket s, MessageManager messageManager,
            List<Socket> clients) {
        this.s = s;
        this.messageManager = messageManager;
        this.clients = clients;
    }

    public void run() {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(
                s.getInputStream()));) {
            while (true) {
                String msg = in.readLine();
                if(count==0){
                    name = msg;
                    messageManager.addMessage(name + "进入了聊天室!");
                    count++;
                }else if (msg.equals("bye")) {
                    messageManager.addMessage(name + "退出了聊天室!");
                    break;
                } else {
                    messageManager.addMessage(name + "说:" + msg);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();            
        } finally {
            try {
                s.close();
            } catch (IOException e) {
            }
            clients.remove(s);
        }
    }
}
class MessageManager{
    private Queue<String> msgQueue;
    private List<Socket> clients;
    public MessageManager(List<Socket> clients){
        msgQueue = new PriorityQueue<>();
        this.clients = clients;
    }
    public synchronized void addMessage(String msg){
        System.out.println("addMessage方法:"+msg);
        msgQueue.add(msg);
        notifyAll();
    }

    public synchronized void sendMessage() {
        while (true) {
            if (msgQueue.size() > 0) {
                String msg = msgQueue.poll();
                System.out.println("发送一条消息:"+msg);

                for (Socket s : clients) {
                    try {
                        PrintWriter out = new PrintWriter(new BufferedWriter(
                                new OutputStreamWriter(s.getOutputStream())), true);
                        out.println(msg);
                        out.flush();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } else {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
    }
}

客户端:Client.java

public class Client {
    public static void main(String[] args) throws IOException {
        Socket s = new Socket("127.0.0.1", 8888);
        ReceiveThread t1 = new ReceiveThread(s);
        t1.start();
        Scanner in = new Scanner(System.in);
        try(PrintWriter out = new PrintWriter(new BufferedWriter(
                new OutputStreamWriter(s.getOutputStream())), true)) {
            System.out.println("请输入您的代号:");
            String name = in.next();
            out.print(name);
            out.flush();
            String msg = null;
            while (true) {
                // 从键盘接收数据,然后发送到服务器
                msg = in.nextLine();
                out.println(msg);
                out.flush();
                if(msg.equals("bye"))
                    System.exit(0);                
            }
        } finally {
            //in.close();
        }
    }
}
class ReceiveThread extends Thread{
    Socket s;
    public ReceiveThread(Socket s){
        this.s = s;
    }
    public void run(){
        try(BufferedReader in = new BufferedReader(new InputStreamReader(
                s.getInputStream()));) {
            while (true) {
                String msg = in.readLine();
                if(msg==null)
                    continue;
                if(msg.equals("bye")){
                    System.out.println("服务器停止服务!");
                    System.exit(0);
                }else{
                    System.out.println(msg);
                }
            }
        } catch(Exception e){
            e.printStackTrace();
        }
    }
}

这个聊天室还是先运行服务器端,然后运行客户端,可以多次运行,每运行一次就代表一个人的操作。
同样也是,我要在服务器上运行服务器端,在pc上多次运行客户端进行聊天

六、用户数据报通信

  • 使用UDP发送数据的过程如下:
    • 创建DatagramSocket对象;
    • 创建DatagramPacket对象;
    • 调用DatagramSocket的方法发送DatagramPacket对象。
  • 使用UDP接收数据的过程如下:
    • 创建DatagramSocket对象;
    • 创建DatagramPacket对象;
    • 接收数据。

数据报是无连接的,也可以来传送数据,所以说这个是主要用来传送数据的。
接收的数据放在数据包里面。
同样也是有服务器端、客户端。
服务器端:Server.java

public class Server {
    // 监听端口
    public static final int PORT = 8888;
    // 接收的每个数据报的最大值为1K
    private static final int LENGTH = 1024;
    // 定义该客户端使用的DatagramSocket
    private DatagramSocket socket = null;
    byte[] buff = new byte[LENGTH];
    // 接收数据的DatagramPacket对象
    private DatagramPacket inPacket = new DatagramPacket(buff, LENGTH);

    public Server() {
        try {
            // 创建一个客户端DatagramSocket
            socket = new DatagramSocket(PORT);
            while (true) {
                socket.receive(inPacket);//接收消息,这个过程没有回的,如果回的话就是服务器向客户端发送数据了,它是无连接
                String temp = new String(inPacket.getData(), 0, 
                        inPacket.getLength());
                if (temp.equals("bye")) {
                    System.out.println("服务器端退出!");
                    break;
                }
                System.out.println("接收到:"+temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null)
                socket.close();
        }
    }

    public static void main(String[] args) {
        new Server();
    }
}

客户端:Client.java

public class Client {
    // 数据包要发往的服务器的端口和IP地址
    public static final int PORT = 8888;
    public static final String IP = "127.0.0.1";
    private DatagramSocket socket = null;
    private DatagramPacket outPacket = null;

    public Client() {
        try {
            // 创建DatagramSocket对象,使用随机端口
            socket = new DatagramSocket();
            // 使用指定的端口和IP创建DatagramSocket对象——————这个就跟聊天室不一样了,这里指定要发送给谁
            outPacket = new DatagramPacket(new byte[0], 0,
                    InetAddress.getByName(IP), PORT);
            // 创建键盘输入流
            System.out.println("请输入信息:");
            Scanner in = new Scanner(System.in);
            // 循环接收键盘输入信息
            while (in.hasNextLine()) {
                System.out.println("请输入信息:");
                // 接收输入
                String temp = in.nextLine();
                if (temp.equals("bye")) {
                    System.out.println("客户端退出!");
                    break;
                }
                byte[] buff = temp.getBytes();
                // 把接收的信息添加到DatagramPacket中
                outPacket.setData(buff);
                // 发送数据报
                socket.send(outPacket);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null)
                socket.close();
        }
    }

    public static void main(String[] args) {
        new Client();
    }
}

以上是关于网络编程的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装

使用 Pygments 检测代码片段的编程语言

面向面试编程代码片段之GC

如何在 Django Summernote 中显示编程片段的代码块?