当前位置: 代码迷 >> 综合 >> 【Java基础】网络编程:Socket、ServerSocket、Echo程序
  详细解决方案

【Java基础】网络编程:Socket、ServerSocket、Echo程序

热度:83   发布时间:2023-12-28 23:53:06.0

《第一行代码:Java》第12章、Java网络编程 读书笔记。有关计算机网络的基础知识建议去看计算机网络相关的文章,本章中主要以“C/S”开发中的TCP程序实现为主。

文章目录

    • Java网络编程
      • 12.1 网络编程
      • 12.2开发第一个网络程序
      • 12.3 网络开发的经典模型——Echo程序
      • 本章小结:

Java网络编程

12.1 网络编程

网络编程的核心意义在于不同的电脑主机之间的数据交互,但是在Java中这一概念会进一步简化。在Java中是以JVM进程划分网络的,即不同的JVM代表不同的主机。Java中同一台主机的不同JVM之间的数据访问也属于远程访问。
在这里插入图片描述

  • 网络编程的是指意义在于数据的交互,而在交互的过程中一定会将交互的双方分为服务器端和客户端,而这两端的开发会存在以下两种模式:

    • 模式一:C/S结构(Client/Server),此类模式的开发一般要编写两套程序,一套是客户端代码,另外一套属于服务器端代码。由于需要有编写程序,所以对于开发以及维护的成本较高。但是由于其使用的是自己的连接端口与交换协议,所以安全性比较高。而C/S结构程序的开发分为两种:

      • TCP(传输控制协议,一种面向连接的通信协议,可靠的传输)
      • UDP(数据报协议,一种面向无连接的通讯协议)。
    • 形式二:B/S结构(Browser /Server ),不再单独开发客户端代码,只开发一套服务器端程序,客户端将利用浏览器进行访问,这种模式只需要开发一套程序,但是安全性不高,因为使用的是公共的HTTP 协议以及公共的80端口。

    本章主要以“C/S”开发中的TCP程序实现为主。

12.2开发第一个网络程序

  • 在java.net包中,提供了网络编程相关的开发工具类,在此包中有以下两个主要的核心操作类。

    • ServerSocket类:是一个封装支持TCP协议的操作类,主要工作是在服务器端,用于接收客户端请求。
    • Socket类:也是一个封装了TCP协议的操作类,每个Socket对象都是一个客户端
  • java.net.ServerSocket类常用方法:

    public ServerSocket(int port) throws IOException	// 构造,开辟一个指定的端口监听,一般使用5000以上的端口
    public Socket accept() throws IOException			// 普通,服务器端接收客户端请求,通过Socket返回
    public void close() throws IOException				// 普通,返回服务器端
    
  • java.net.Socket类常用方法:

    public Socket(String host, int port) throws UnknowHostException, IOException	// 构造,指定要连接的主机(IP地址)和端口
    public OutputStream getOutputStream() throws IOException	// 普通,取得指定客户端的输出对象,使用PrintStream操作
    public InputStream getInputStream() throws IOException		// 普通,从指定的客户端读取数据,使用Scanner操作
    
  • 在客户端,程序可以通过Socket类的getInputStream()方法,取得服务器的输出信息,在服务器端可以通过getOutputStream()方法取得客户端的输出信息,具体如下图所示:
    在这里插入图片描述

  • 定义服务器端——主要使用ServerSocket类:

    package com.yootk.demo;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    public class HelloServer {
           public static void main(String[] args) throws Exception {
          ServerSocket server = new ServerSocket(9999) ;	// 所有的服务器必须有端口System.out.println("等待客户端连接.....");			// 提示信息Socket client = server.accept() ;				// 等待客户端连接// OutputStream并不方便进行内容的输出,所以利用打印流完成输出PrintStream out = new PrintStream(client.getOutputStream()) ;out.println("Hello World !");					// 输出数据out.println("你好世界 !");						 // 输出数据out.close(); client.close();server.close();}
    }
    // 程序执行结果: 等待客户端连接.....
    

    本程序在本机的9999端口上设置了一个服务器的监听操作(accept()方法表示打开服务器监听,并且会一直等待,直到有客户端连接后才会执行下一步),当有客户端通过TCP连接方式连接到服务器端后,服务器端将会利用PrintSream输出数据,当数据输出完毕后,该服务器将会关闭,也就是说本次定义的服务器只能处理一次客户端的请求。

  • 编写客户端——主要使用Socket类:

    package com.yootk.demo;
    import java.net.Socket;
    import java.util.Scanner;
    public class HelloClient {
          public static void main(String[] args) throws Exception {
          Socket client = new Socket("localhost",9999) ;			// 连接服务器端// 取得客户端的输入数据流对象,表示接收服务器端的输出信息Scanner scan = new Scanner(client.getInputStream()) ;	// 接收服务器端回应数据scan.useDelimiter("\n") ;								// 设置分隔符while (scan.hasNext()) {
          								// 是否有数据System.out.println("【回应数据】" + scan.next());		// 取出数据}scan.close();client.close();}
    }
    /* 程序执行结果:Hello World !你好世界 ! */
    

    在TCP程序中,每一个Socket对象都表示一个客户端的信息,所以客户端程序要连接也必须依靠Socket对象操作。在实例化Socket对象时必须设置连接的主机名称(本机为“localhost“,或者填写IP地址)以及连接端口号,当连接成功后就可以利用Scanner进行输入流数据的读取,这样就可以接收服务器端的回应信息了。

12.3 网络开发的经典模型——Echo程序

在网络编程中 Echo是一个经典的程序开发模型,本程序的意义在于:客户端随意输入信息并且将信息发送给服务器端,服务器端接收后前面加上一个”ECHO:”的前缀标记后将数据返还给客户端。在本程序中服务器端既要接收客户端发送来的数据,又要向客户端输出数据,同时考虑到需要进行多次数据交换,所以每次连接后不应该立刻关闭服务器,而当用户输入了一些特定字符串(例如:“byebye”)后才表示可以结束本次的Echo操作。

  • 服务器端实现:

    package com.yootk.demo;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    public class EchoServer {
          public static void main(String[] args) throws Exception {
          ServerSocket server = new ServerSocket(9999) ;	// 定义连接端口Socket client = server.accept() ;				// 等待客户端连接// 得到客户端输入数据以及向客户端输出数据的对象,利用扫描流接收,打印流输出Scanner scan = new Scanner(client.getInputStream()) ;PrintStream out = new PrintStream(client.getOutputStream()) ;boolean flag = true ;							// 设置循环标记while(flag) {
          if (scan.hasNext()) {
          						// 是否有内容输入String str = scan.next().trim() ;		// 得到客户端发送的内容,并删除空格if (str.equalsIgnoreCase("byebye")) {
          	// 程序结束标记out.println("拜拜,下次再会!");		   // 输出结束信息flag = false ;						// 退出循环} else {
          									// 回应输入信息out.println("ECHO : " + str);			// 加“ECHO :”前缀返回}}}scan.close();out.close();client.close(); server.close();}
    }
    
  • 客户端实现:

    package com.yootk.demo;
    import java.io.PrintStream;
    import java.net.Socket;
    import java.util.Scanner;
    public class EchoClient {
          public static void main(String[] args) throws Exception {
          Socket client = new Socket("localhost", 9999);		// 服务器地址与端口Scanner input = new Scanner(System.in); 			// 键盘输入数据// 利用Scanner包装客户端输入数据(服务器端输出),PrintStream包装客户端输出数据;Scanner scan = new Scanner(client.getInputStream());PrintStream out = new PrintStream(client.getOutputStream());input.useDelimiter("\n");						// 设置键盘输入分隔符scan.useDelimiter("\n");						// 设置回应数据分隔符boolean flag = true;							// 循环标志while (flag) {
          System.out.print("请输入要发送数据:");if (input.hasNext()) {
          						// 键盘是否输入数据String str = input.next().trim();		// 取得键盘输入数据out.println(str); 						// 发送数据到服务器端if (str.equalsIgnoreCase("byebye")) {
          	// 结束标记flag = false; 						// 结束循环}if (scan.hasNext()) {
          					// 服务器端有回应System.out.println(scan.next()); 		// 输出回应数据}}}input.close();scan.close();out.close();client.close();}
    }
    /* 程序执行结果:请输入您需要发送的数据:aewcdsacsa5545Echo:aewcdsacsa5545请输入您需要发送的数据:54d5f4adEcho:54d5f4ad请输入您需要发送的数据:sdafsa4Echo:sdafsa4请输入您需要发送的数据:byebye拜拜,下次再见! */
    
  • 上面的程序实现了一个最简单的服务器端与客户端通讯,但是该程序只能连接一个客户端,不能连接其他客户端,因为所有的操作都是在主线程上进行的开发,也就是说该程序属于单线程的网络应用。而在实际的开发中一个服务器需要同时处理多个客户端的请求操作,在这样的情况下就可以利用多线程来进行操作,把每一个连接到服务器端的客户都作为一个独立的线程对象保留,如图12-3所示:
    在这里插入图片描述

  • 多线程服务器端实现:

    package com.yootk.demo;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    class EchoThread implements Runnable {
          					// 建立线程类private Socket client;								// 每个线程处理一个客户端public EchoThread(Socket client) {
          					// 创建线程对象时传递Socketthis.client = client;}@Overridepublic void run() {
          	try {
          	// 每个线程对象取得各自Socket的输入流与输出流Scanner scan = new Scanner(client.getInputStream());PrintStream out = new PrintStream(client.getOutputStream());boolean flag = true; 							// 控制多次接收操作while (flag) {
          if (scan.hasNext()) {
          						// 是否有内容String str = scan.next().trim(); 		// 得到客户端发送的内容if (str.equalsIgnoreCase("byebye")) {
           	// 程序结束out.println("拜拜,下次再会!");flag = false; 						// 退出循环} else {
           								// 应该回应输入信息out.println("ECHO : " + str);			// 回应信息}}}scan.close();out.close();client.close();} catch (Exception e) {
          e.printStackTrace();}}
    }
    public class EchoServer {
          public static void main(String[] args) throws Exception {
          ServerSocket server = new ServerSocket(9999);	// 在9999端口上监听boolean flag = true;							// 循环标记while (flag) {
          									// 接收多个客户端请求Socket client = server.accept(); 			// 客户端连接new Thread(new EchoThread(client)).start();	// 创建并启动新线程}server.close();}
    }
    

本章小结:

  • ServerSocket主要用于TCP协议的服务器程序开发上,使用accept()方法等待客户端连接,每个连接的客户端都是用一个SOcket表示。
  • 服务器端加入多线程机制后,就可以同时为多个用户提供服务。
  相关解决方案