跳至主要內容

网络编程

wangdx大约 6 分钟

网络编程

使用网络连接的目的是为了可以实现所有不同主机之中的数据共享,早期的共享是属于主动共享,而现在的大数据的共享严格意义上来说属于被动共享。如果真的要去聊网络程序开发的话,那么首先就必须来回顾一下网络之中的 OSI 七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层,每一层都有不同数据的处理逻辑。

TCP 五层模型

而所谓的网络编程现在更多指的是针对于“TCPUDP”两种协议进行的网络开发,但是由于这两个协议本身所涉及到的细节性的组成是非常繁琐的,如果你个人不是精通于网络协议,那么这样对于开发是非常麻烦的,所以为了解决这样的网络程序的开发问题,才针对于各种网络协议进行了一种抽象性管理,而这种抽象性的程序管理逻辑:Socket 编程,而在 Socket 编程内部针对于 网络程序的引用模式也分为两种形式:

  • C/S(Client/Server)结构:需要开发两套程序,一套是服务器端程序,另外一套是客户端程序,在进行维护的时候服务器端程序和客户端的程序都需要进行更新,这种操作使用特定的数据传输协议(由用户自己定义)并且使用的是一些非公开的端口,所以程序的安全性比较高;:
  • B/S(Browser/Server)结构:基于浏览器实现客户端应用,只需要开发一套服务器端的程序即可,如果要进行维护升级只需要修改服务器端的程序代码即可完成,这种操作机制最大的特点是维护与开发的成本降低,同时由于使用的是公共的处理协议(HTTP 协议,是基于 TCP 协议的一种实现),所以安全性较差。

本次所讲解的网络编程属于 C/S 程序模型,而像 WEB 开发就属于 B/S 程序模型,而对于 C/S 的网络程序模型来讲也分为两种 开发模型:

  • TCP 程序:使用三次握手和四次挥手的方式保证所有的数据可靠的进行传输;
  • UDP 程序:发送数据报,而接收数据报的一方不一定可以接收到信息。

开发网络程序

如果要想进行网络程序的开发,最简单的应用实现就可以直接通过 iava.net 开发包来完成,在这个包中提供有两个类:

  • java.net.ServerSocket:工作在服务端的程序类,主要是定义服务监听端口,以及接收客户端请求。

  • java.net.Socket:每一个客户端都使用一个 Socket 的概念进行描述;

服务端范例

详情
package com.yix.stream.w07.server;

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author wangdx
 */
public class HelloServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("【HelloServer】服务端启动监听");
        //每一个连接到服务器端的客户都通过Socket对象来描述,所以此时要等待连接
        Socket client = serverSocket.accept();
        // 向指定的socket实现数据的输出,所以应该获取Socket输出流
//        PrintWriter out = new PrintWriter(client.getOutputStream()); //字符
        PrintStream out = new PrintStream(client.getOutputStream());
        out.println("www.yix.com");
//        client.close();
        client.shutdownOutput(); // 数据发送完毕再关闭
        serverSocket.close();
    }
}

如果现在要想验证服务器是否可以正常使用,那么最简单的做法是直接利用操作系统中的 tenet 命令来完成(如果是 Windows 系统,对于 telnet 命令都需要进行额外的安装)。

提示

window10 开启 telnet

一、使用 win+R 键打开运行程序,在输入框里输入:OptionalFeatures 点击确定

二、勾选上 Telnet 客户端并点击确定,就会开始安装 telnet 客户端

三、重新打开一个 cmd 窗口输入 telnet 命令,如下所示表明 telnet 客户端安装成功

四、使用测试端口是否开启

telnet 给出的仅仅是一个基础的测试环境,但是对于整个的程序来讲对于网络程序应该提供有一个专属的客户端代码,那么下面就通过 Socket 类实现客户端的处理,需要注意的是:服务器端的数据输出实际上就属干客户端数据的输入。

客户端范例

详情
package com.yix.stream.w07.client;

import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author wangdx
 */
public class HelloClient {
    public static void main(String[] args) throws IOException {
        //Socket是工作在客户端的程序类,每一个Socket对象描述的都是一个独立的客户端
        Socket client = new Socket("localhost", 9999);
        Scanner scanner = new Scanner(client.getInputStream());//服务器端的输出为客户端的输入
        if (scanner.hasNext()) {
            System.out.println("【HelloClient】" + scanner.next());
        }
        client.shutdownOutput();// 全部输入完成后再关闭客户端Socket
    }
}

现在整个的程序就实现了 一个自定义的网络客户端,所以可以发现 Socket 类描述的就属于客户端的信息,在客户端主要用它 来实现服务器端的连接,而在服务器端每一个连接到服务器的客户都通过 Socket 来描述

Echo 程序模型

Echo 模型可以理解为现在整个网络通讯程序的核心结构,现在的所有的通讯都是基于这种 Echo 模型衍生而来的,所谓的 ECHO 模型最大的特点在于:客户端发送数据给服务器端,服务器端接收到此数据之后直接进行回应,并且这种回应的处理可以持续进行,当客户端确定不再需要继续进行交互的时候则断开整个的服务器连接。

  • 首先实现服务器端程序的开发,由于现在的数据需要持续性的进行交互,所以应该设置有一个数据的终止判断;
详情
package com.yix.stream.w07.echo.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author wangdx
 */
public class EchoServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        boolean flag = true;
        while (flag) {
            Socket client = serverSocket.accept();
            new Thread(new EchoHandler(client)).start(); // 启动线程
        }
        serverSocket.close();
    }
}
package com.yix.stream.w07.echo.server;

import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author wangdx
 */
public class EchoHandler implements Runnable {
    private Socket client;

    public EchoHandler(Socket client) {
        this.client = client;
    }

    @Override
    public void run() {
        try {
            Scanner scanner = new Scanner(client.getInputStream());// 客户端输入流
            PrintStream out = new PrintStream(client.getOutputStream());// 客户端输出流
            boolean flag=true;
            while (flag){
                if(scanner.hasNext()){
                    String value = scanner.next().trim();
                    if(value.equalsIgnoreCase("exit")){
                        out.println("【EchoServer】信息服务交互完毕,已经断开与服务器的连接。");
                        flag = false; // 停止交互
                        break; // 后续代码不再执行了
                    }
                    out.println("ECHO服务端收到:" + value);
                }
            }
        } catch (Exception e) {
        }
    }
}
  • 键盘输入工具类
详情
package com.yix.stream.w07.util;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author wangdx
 */
public class KeyboardInputUtil {
    private static final BufferedReader INPUT = new BufferedReader(new InputStreamReader(System.in));

    private KeyboardInputUtil() {
    }

    public static String getString(String prompt) {
        System.out.print(prompt);
        try {
            String value = INPUT.readLine(); // 读取数据内容
            return value;
        } catch (Exception e) {
            return null;
        }
    }
}
  • 客户端服务
详情
package com.yix.stream.w07.client;

import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author wangdx
 */
public class HelloClient {
    public static void main(String[] args) throws IOException {
        //Socket是工作在客户端的程序类,每一个Socket对象描述的都是一个独立的客户端
        Socket client = new Socket("localhost", 9999);
        Scanner scanner = new Scanner(client.getInputStream());//服务器端的输出为客户端的输入
        if (scanner.hasNext()) {
            System.out.println("【HelloClient】" + scanner.next());
        }
        client.shutdownOutput();// 全部输入完成后再关闭客户端Socket
    }
}

此时的操作代码就实现了一个最为核心的网络基础的交互模型,而这个模型如果再继续扩展,则可以实现更加丰富的内容的 传输,几乎所有的聊天工具都使用了以上的模型。

BIO 处理模型

BIO(Blocking IO、阻塞 IO 处理)是最为传统的一种网络通讯模型的统一描述,这种通讯模型主要是为了解决服务器的并发处理问题,通过之前的程序已经成功的实现了一个 ECHO 交互模型,但是这个模型本身会存在有一个问题:当前的操作属于单线程服务器的开发,也就是说同一个时间段内只能够有一个线程进行访问。

详情
package com.yix.stream.w07.echo.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author wangdx
 */
public class EchoServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        boolean flag = true;
        while (flag) {
            Socket client = serverSocket.accept();
            new Thread(new EchoHandler(client)).start(); // 启动线程
        }
        serverSocket.close();
    }
}
package com.yix.stream.w07.echo.server;

import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author wangdx
 */
public class EchoHandler implements Runnable {
    private Socket client;

    public EchoHandler(Socket client) {
        this.client = client;
    }

    @Override
    public void run() {
        try {
            Scanner scanner = new Scanner(client.getInputStream());// 客户端输入流
            PrintStream out = new PrintStream(client.getOutputStream());// 客户端输出流
            boolean flag=true;
            while (flag){
                if(scanner.hasNext()){
                    String value = scanner.next().trim();
                    if(value.equalsIgnoreCase("exit")){
                        out.println("【EchoServer】信息服务交互完毕,已经断开与服务器的连接。");
                        flag = false; // 停止交互
                        break; // 后续代码不再执行了
                    }
                    out.println("ECHO服务端收到:" + value);
                }
            }
        } catch (Exception e) {
        }
    }
}

UDP 程序

在使用 TCP 程序进行数据交互的操作过程之中,所有的处理机制都采用的是可靠的连接模式,所以一定可以保证数据发送和接收稳定性,但是可靠的连接所带来的问题就是严重的性能损耗,所以在网络的编程模型里面又提供有另外一种协议的开发: UDP 基于数据报的协议开发的,这种方式最大的特点是不保证用户一定可以收到数据。"

详情
package com.yix.stream.w07.udp.client;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @author wangdx
 */
public class UDPClient {
    public static void main(String[] args) throws Exception {
        DatagramSocket client = new DatagramSocket(9999) ; // 设置客户端的监听端口
        byte data[] = new byte[1024]; // 准备接收信息
        DatagramPacket packet = new DatagramPacket(data, data.length); // 准备进行数据的接收
        System.out.println("【UDPClient】客户端等待服务器端发送消息 ..........");
        client.receive(packet); // 接收消息,如果没有接收到就一直等待
        System.out.println("【UDPClient】接收到消息,消息内容为:" + new String(data, 0, packet.getLength()));
    }
}
package com.yix.stream.w07.udp.server;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author wangdx
 */
public class UDPServer {
    public static void main(String[] args)  throws Exception  {
        DatagramSocket server = new DatagramSocket(8000);
        String message = "www.yootk.com"; // 当前要发送的数据内容
        DatagramPacket packet = new DatagramPacket(message.getBytes(),0,message.length(), InetAddress.getByName("localhost"),9999);// 创建数据报
        server.send(packet); // 发送数据报
        System.out.println("【UDPServer】数据信息发送完毕 ....");
        server.close();
    }
}
上次编辑于: