内容学习于:edu.aliyun.com


1. 网络编程简介

  Java语言走到今天这一个层次上实际上其最大的技术发挥点就在于网络编程上,Java 拥有良好的性能,尤其是其优秀的多线程并发支持能力,更是其它语言所无法达到的一个高度。
  网络的定义:把物理上分离的计算机通过某种途径连接在一起。
  如下图所示:
  但是如果要进行网络开发,并不一定非要去找到两台不同的电脑,在Java里面,每一一个JVM的进程实际上都是一***立的虚拟电脑,所以不同JVM之间的通讯操作实际上就属于远程访问,也就是说你可以创建两个JVM进程,一个作为服务器端,另外一个作为客户端。
  在Java原生支持里面(JDK)之中支持的网络开发有两个主要的协议应用: TCP (传输控制协议,属于可靠的连接服务)程序、UDP(数据报协议属于不可靠的连接服务)程序,现在使用最多的HTTP协议就是在TCP的基础上构建的(未来的HTTP可以基于UDP协议构建)。

  在进行网络程序开发的过程里面,也分为两种开发模式:

  • C/S (Client/Service) :需要开发两套程序,一个是客户端程序,另外一个是服务器端程序,这种模式使用的是私有的传输模式,所以其安全性是比较高的;
  • B/S (Browse/Service) :只开发服务器一套程序,而客户端使用浏览器进行访问,由于使用的是公共协议,所以其安全性较差,但是维护性比较强。

2. 开发TCP服务器

  如果要进行网络服务器的开发,那么一定要掌握java.net开发包中提供的两个类: ServerSocket、 Socket。如下图所示:

编写服务器端代码:

public class JavaAPIDemo {

    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9999);//设置绑定端口号,使用8000以上的端口
        System.out.println("服务器端程序运行,等待客户端连接......");
        Socket client = server.accept();//等待服务器端连接
        PrintStream out = new PrintStream(client.getOutputStream());//获取客户端的输出对象
        out.print("hello");//输出信息
        out.close();
        server.close();
    }

}

结果:

服务器端程序运行,等待客户端连接…

  此时的服务器并没有关闭,而是等待客户端连接执行,如果客户端不连接,那么服务器端将持续进行等待,并且连接的客户端只获取一个提示信息就可以结束所有的操作,为了简化测试,可以直接使用操作系统自带的telnet命令来完成。
  如下图安装该命令的程序:

  • 1、输入“telnet"启动程序命令;
  • 2、连接服务器端: open IP 地址端口号
连接IP 连接本机
open 192.168.190.1 9999 open localhost 9999、open 127.0.0.1 9999

  如下图所示:

3. 开发网络客户端

  之前的服务器端的程序是利用了telnet第三方命令实现的连接,而在实际的编写之中肯定需要编写单独的客户端程序,客户端主要是依靠的是Socket 类实现的连接,此类的构造方法如下:

  • public Socket(String host,int port) throws UnknownHostException, IOException

编写客户端代码:

public class HelloClient {
    public static void main(String[] args) throws Exception{
        Socket client = new Socket("localhost",9999);//连接
        Scanner in = new Scanner(client.getInputStream());//获取服务器的输入
        in.useDelimiter("\n");//设置分割符
        if (in.hasNext()){
            System.out.println(in.next());
        }
        client.close();
        in.close();
    }
}

结果:

Hello

  此时已经完整的证明了之前给出的结论: ServerSocket 工作在服务器端,而Socket工作在客户端,对于服务器端而言,可以通过Socket描述每一个具体的客户端信息。

4. Echo程序模型

  Echo程序属于一个经典的网络通讯模型案例,即:客户端通过键盘输入信息,并且将信息发送给服务器端,随后服务器端接收到数据之后在前面追加一个“ECHO"信息返回内容。
  如下图所示:

编写Echo服务端代码:

public class EchoServer {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9988);
        boolean flag = true;
        System.out.println("服务器等待客户端进行访问......");
        Socket client = server.accept();
        PrintStream out = new PrintStream(client.getOutputStream());//对用户的输出
        Scanner scanner = new Scanner(client.getInputStream());//获取用户的输入
        scanner.useDelimiter("\n");
        while (flag) {
            if (scanner.hasNext()) {
                String str = scanner.next().trim();
                if ("88".equals(str)) {//交互结束
                    out.println("【ECHO】" + "再见!!!");
                    flag = false;
                } else {
                    out.println("【ECHO】" + str);
                }
            }
        }
        client.close();
        scanner.close();
        server.close();
    }

}

结果:

服务器等待客户端进行访问…

编写Echo客户端代码:

public class EchoClient {
    public static void main(String[] args) throws Exception{
        Socket client = new Socket("localhost",9988);
        PrintStream out = new PrintStream(client.getOutputStream());//对用户的输出
        Scanner scanner = new Scanner(client.getInputStream());//获取用户的输入
        scanner.useDelimiter("\n");
        boolean flag = true;
        while (flag){
            String str = InputUtil.getString("请输入消息:");
            out.println(str);
            if (scanner.hasNext()){//如果有内容返回
                System.out.println(scanner.next().trim());
            }
            if("88".equals(str)){
                flag = false;
            }
        }
        scanner.close();
        client.close();
        out.close();
    }
}

结果:

请输入消息:
hello
【ECHO】hello
请输入消息:
你好!
【ECHO】你好!
请输入消息:
88
【ECHO】再见!!!

5. 多线程与Echo模型(BIO模型)

  现在已经成功的实现了一个服务器端和客户端的持续交互,但是此时的操作属于单线程应用。对于单线程应用的最大特点在于,如果现在已经有一个客户端连接上了,那么所有的服务器的资源就只会为一个客户端服务,而其它的客户端是无法进行连接的,所以要想实现并行的用户处理就必须引入多线程。如下图所示:

  此时的程序不需要进行客户端的修改,只需要处理服务器端的操作即可,引入线程处理类。

修改服务端代码:

class ClientThread implements Runnable{
    private Socket client;
    public ClientThread(Socket client){
        this.client = client;
    }

    @Override
    public void run() {
       try{
           boolean flag = true;
           PrintStream out = new PrintStream(client.getOutputStream());//对用户的输出
           Scanner scanner = new Scanner(client.getInputStream());//获取用户的输入
           scanner.useDelimiter("\n");
           while (flag) {
               if (scanner.hasNext()) {
                   String str = scanner.next().trim();
                   if ("88".equals(str)) {//交互结束
                       out.println("【ECHO】" + "再见!!!");
                       flag = false;
                   } else {
                       out.println("【ECHO】" + str);
                   }
               }
           }
           client.close();
           scanner.close();
       }catch (Exception e){}
    }
}


public class EchoServer {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9988);
        boolean flag = true;
        System.out.println("服务器等待客户端进行访问......");
        while (flag){
            Socket client = server.accept();
            new Thread(new ClientThread(client)).start();
        }

        server.close();
    }

}

6. UDP通讯

  在之前所讲解的都属于TCP程序,所有的TCP程序都基于三方握手的实现机制,所以可以保证可靠的传输通讯,但是除了TCP之外,还存在有一种UDP程序,UDP程序的最大特点在于,所有的消息使用广播机制来进行传输,那么客户端可能收的到也可能收不到,它实现的一种不可靠的连接,但是很明显UDP要比TCP节约资源

编写UDP客户端代码:

public class UDPClient {
    public static void main(String[] args) throws Exception{
        DatagramSocket socket = new DatagramSocket(9999);//连接到指定的监听端口
        byte [] data = new byte[1024];
        DatagramPacket packet = new DatagramPacket(data,data.length);//
        socket.receive(packet);//等待接收消息
        System.out.println("【UDP】服务端发送的消息为:" + new String(data,0,packet.getLength()));
        socket.close();
    }
}

编写UDP服务端代码:

public class UDPServer {
    public static void main(String[] args) throws Exception {
        DatagramSocket server = new DatagramSocket(9900);
        String str = "www.mldn.cn";
        DatagramPacket packet = new DatagramPacket(str.getBytes(), 0, str.length(), InetAddress.getByName("localhost"), 9999);
        server.send(packet);
        server.close();
    }
}

结果:

【UDP】服务端发送的消息为:www.mldn.cn

  此时的客户端如果不启动将无法接收到服务器端发送来的消息内容,也就是说此时的发送属于不可靠的连接。