• Java 网络编程


    Java 网络编程

    1、网络编程的基础知识

    1.1、网络基础知识

    计算机网络的功能、计算机网络的分类、osi分层模型和tcp/ip分层模型等这些基础知识在这里就不过多阐述,可以自己网上查资料了解。因为讲太细内容就很多,写不过来。讲个大概,还不如自己网上查资料看。

    1.2、IP地址和端口号

    这个也是自己网上查资料学习,我们的侧重点不在这里

    2、使用InetAddress

    Java类提供了InetAddress类来代表IP地址,InetAddress下还有两个子类:Inet4Address、Inet6Address,它们分别代表IPv4地址和IPv6地址

    下面根据代码来简单学习一下InetAddress的使用

     public static void main(String[] args) throws IOException {
           //获取InetAddress实例
           InetAddress inetAddress=InetAddress.getLocalHost();
           //判断地址是否可达
           boolean reach=inetAddress.isReachable(2000);
           System.out.println(reach);
           //获取主机名
           String hostName=inetAddress.getHostName();
           System.out.println(hostName);
           //获取ip
           String ip=inetAddress.getHostAddress();
           System.out.println(ip);
      }

    3、使用URLEncoder和URLDecoder

    URLEncoder和URLDecoder用于完成普通字符串和application/x-www-form-urlencoded MIME字符串(百度地址栏的乱码字符串)之间的相互转换。

    下面根据代码来简单学习一下这两个类的使用

    public static void main(String[] args) throws                       UnsupportedEncodingException {
          //将普通字符串转换成application/x-www-form-urlencoded字符串
           String value= URLEncoder.encode("胡图图","UTF-8");
           System.out.println(value);
          //将application/x-www-form-urlencoded字符串转换成普通字符串
           String value1= URLDecoder.decode(value,"UTF-8");
           System.out.println(value1);
      }

    4、基于TCP协议的网络编程

    TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行通信。

    IP协议保证了计算机之间可以发送和接收数据,但不能解决数据分组在传输过程中可能出现的问题;TCP协议为计算机通信建立连接,提供可靠并且无差错的通信服务

    TCP协议使用重发机制——当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体的确认信息,如果没有收到另一个通信实体的确认信息,则会再次重发刚才发送的信息

    img

    5、ServerSocket和Socket

    ServerSocket用于监听来自客户端的Socket连接(创建服务器)

    Socket用于连接到指定服务器(创建客户端)

    下面根据代码来简单学习一下这两个类的使用

    public class Server {

       public static void main(String[] args) throws IOException {
           //服务器(不指定ip默认本机ip),端口通常使用1024以上的,避免与其他应用程序的通用端口冲突
           ServerSocket serverSocket=new ServerSocket(10086);
           //一直接收客户端请求
           while (true){
               //等待连接
               Socket socket=serverSocket.accept();
               //获取对应输出流包装成打印流
               PrintStream printStream=new PrintStream(socket.getOutputStream());
               //打印数据
               printStream.println("你好,客户端");
               //关闭流
               printStream.close();
               serverSocket.close();
          }
      }

    }
    public class Client {

       public static void main(String[] args) throws IOException {
           //客户端
           Socket socket=new Socket("192.168.59.72",10086);
           //获取对应输入流并包装成BufferedReader
           BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
           //读取数据
           String content=bufferedReader.readLine();
           System.out.println("来自服务器:"+content);
           //关闭资源
           bufferedReader.close();
           socket.close();
      }

    }
    /**
    *上面程序为了突出通过ServerSocket和Socket建立连接,并通过底层IO流进行*通信的主题,没有进行异常处理,也没有使用finally块来关闭资源
    */

    6、C/S聊天室应用

    实现一个命令行界面的C/S聊天室应用,服务器端应包含多个线程,每个Socket对应一个线程,该线程负责读取Socket对应输入流的数据(从客户端发送过来的数据),并将读到的数据向每个Socket输出流发一次(将一个客户端发送的数据“广播”给其他客户端)

    /**
    * 服务器端
    */
    public class Server {
       //客户端容器
       public static List<Socket> socketList=new ArrayList<>();

       public static void main(String[] args) throws IOException {
           //服务器
           ServerSocket serverSocket=new ServerSocket(10087);
           while (true){
               //等待客户端连接
               Socket socket=serverSocket.accept();
               //将获取的客户端放入socketList
               socketList.add(socket);
               //处理请求
               new Thread(new ServerThread(socket)).start();
          }
      }

    }
    /**
    * 处理客户端请求
    */
    public class ServerThread implements Runnable{
       private Socket socket;//客户端
       private BufferedReader bufferedReader;//输入流
       private PrintStream printStream;//打印流

       public ServerThread(Socket socket) {
           this.socket = socket;
      }
       
       /**
        * 线程主体
        */
       @Override
       public void run() {
           try {
               //获取客户端对应输入流
               bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
               //存储读取的数据
               String content=null;
               //读取数据
               while ((content=bufferedReader.readLine())!=null){
                   //遍历客户端容器
                   for (Socket s:Server.socketList){
                       //获取s对应的输出流
                       printStream=new PrintStream(s.getOutputStream());
                       //写数据到客户端对应的流
                       printStream.println(content);
                  }
              }
          } catch (IOException e) {
               //将此客户端从容器移除
               Server.socketList.remove(socket);
          } finally {
               //关闭资源
               try {
                   printStream.close();
                   bufferedReader.close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
      }
    }
    /**
    * 客户端
    */
    public class Client {

       public static void main(String[] args) throws IOException {
           //客户端
           Socket socket=new Socket("127.0.0.1",10087);
           //处理服务器响应
           new Thread(new ClientThread(socket)).start();
           //输入流
           BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
           //获取客户端对应输出流
           PrintStream printStream=new PrintStream(socket.getOutputStream());
           //存储读取数据
           String content=null;
           //读取用户输入数据
           while ((content=bufferedReader.readLine())!=null){
               //将读取的数据写入服务器对应的流
               printStream.println(content);
          }
      }
    }
    /**
    * 处理服务器响应
    */
    public class ClientThread implements Runnable{
       private Socket socket;//客户端
       private BufferedReader bufferedReader;//输入流

       public ClientThread(Socket socket) {
           this.socket = socket;
      }

       @Override
       public void run() {
           try {
               //获取客户端对应输入流
               bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
               //存储读取的数据
               String content=null;
               //读取数据
               while ((content=bufferedReader.readLine())!=null){
                   //打印数据
                   System.out.println(content);
              }

          } catch (IOException e) {
               e.printStackTrace();
          }
      }
    }

    如果使用bufferedWriter写一行数据到Socket对应的流中,必须要有换行,因为服务器和客户端通信时,总是以行作为通信的最小数据单位,换行表示输出数据结束,可以进行读取

    7、半关闭的Socket

    Socket提供了两个半关闭的方法,只关闭Socket的输入流或者输出流,用以表示输出数据已经发送完成

    • shutdownInput():关闭该Socket的输入流,程序还可以通过该Socket的输出流输出数据

    • shutdownOutput():关闭该Socket的输出流,程序还可以通过该Socket的输入流读取数据

    注意:即使同一个Socket实例先后调用这两个半关闭方法,该Socket实例依然没有关闭,只是该Socket既不能输出数据,也不能读取数据而已

    public static void main(String[] args) throws IOException {
           //服务器
           ServerSocket serverSocket=new ServerSocket(10088);
           //等待客户端连接
           Socket socket=serverSocket.accept();
           //获取对应输出流并包装成打印流
           PrintStream printStream=new PrintStream(socket.getOutputStream());
           //写数据到客户端对应的流
           printStream.println("服务器的第一行数据");
           printStream.println("服务器的第二行数据");
           //关闭socket的输出流,表示输出结束
           socket.shutdownOutput();
           //客户端输入流
           Scanner scanner=new Scanner(socket.getInputStream());
           //读取数据
           while (scanner.hasNextLine()){
               //输出
               System.out.println(scanner.nextLine());
          }
           //关闭资源
           scanner.close();
           socket.close();
           serverSocket.close();
      }

    当调用shutdownInput()或shutdownOutput后,该Socket的输出流或输入流无法再次打开,因此这种做法通常不适合保持持久通信装态的交互式应用,只适用于一站式的通信协议,例如HTTP协议——客户端连接到服务器后,开始发送请求数据,发送完成后无需再次发送数据,只需要读取服务器响应数据即可,当读取完成后,该Socket连接也关闭了

    8、UDP协议应用

    8.1、UDP协议概述

    • 用户数据报协议:基于数据报,以数据报为单位来传输

    • 传输不可靠的数据协议:传输过程中可能存在丢失

    • 非面向连接协议

    • 它没有很明确的客户端、服务端之分

    8.2、UDP协议的优缺点

    • 缺点:数据不可靠

    • 优点:传输效率高

    9、UDP核心类

    9.1、 DatagramSocket

    • 此类表示用于发送和接收数据报数据包的套接字。

    • DatagramSocket的构造器

      • DatagramSocket():不绑定端口,是发送方

      • DatagramSocket(int port):绑定端口,是接收方

    9.2 DatagramPacket

    • 该类表示数据报包

    • DatagramPacket的构造器

      • DatagramPacket(byte[] buf, int length):接收长度为length的数据包保存到buf

      • DatagramPacket(byte[] buf, int length, InetAddress address, int port):将长度为length的数据存入buf发送到指定主机的指定端口

    9.3案例代码1

    需求:主机A发送字符串数据给主机B接收,通过UDP协议实现

    public class Receive {

       public static void main(String[] args) throws IOException {
           //接收方
           DatagramSocket datagramSocket=new DatagramSocket(10086);
           //存储接收内容
           byte[] bytes=new byte[1024];
           //接收的数据包
           DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length);
           //等待接收数据
           datagramSocket.receive(datagramPacket);
           //获取接收数据并转化成字符串
           String receive=new String(datagramPacket.getData(),0,datagramPacket.getLength());
           //输出接收数据
           System.out.println(receive);
      }

    }
    public class Send {

       public static void main(String[] args) throws IOException {
           //发送方
           DatagramSocket datagramSocket=new DatagramSocket();
           //发送的内容
           String send="lsy";
           //发送的数据包
           DatagramPacket datagramPacket=new DatagramPacket(send.getBytes(),send.getBytes().length,
                   InetAddress.getByName("127.0.0.1"),10086);
           //发送数据
           datagramSocket.send(datagramPacket);
      }

    }

    9.4代码案例2

    需求:基于UDP协议实现点对点的聊天功能

    public class A {
       //端口号
       public static final int PORT=10086;

       public static void main(String[] args) throws IOException {
           //输入扫描对象
           Scanner scanner=new Scanner(System.in);
           //发送方
           DatagramSocket sendDS=new DatagramSocket();
           //接收方
           DatagramSocket receiverDS=new DatagramSocket(PORT);
           //启动线程处理接收数据
           new ReadThread(receiverDS).start();
           System.out.println("请输入发送的内容:");
           //不停发送数据
           while (true){
               //用户输入并获取输入的数据
               String send=scanner.next();
               //发送的数据报
               DatagramPacket datagramPacket=new DatagramPacket(send.getBytes(),send.getBytes().length,
                       InetAddress.getByName("127.0.0.1"),B.PORT);
               //发送数据
               sendDS.send(datagramPacket);
          }
      }

    }
    public class B {
       //端口号
       public static final int PORT=10087;

       public static void main(String[] args) throws IOException {
           //输入扫描对象
           Scanner scanner=new Scanner(System.in);
           //发送方
           DatagramSocket sendDS=new DatagramSocket();
           //接收方
           DatagramSocket receiverDS=new DatagramSocket(PORT);
           //启动线程处理接收数据
           new ReadThread(receiverDS).start();
           System.out.println("请输入发送的内容:");
           //不停发送数据
           while (true){
               //用户输入并获取输入的数据
               String send=scanner.next();
               //发送的数据报
               DatagramPacket datagramPacket=new DatagramPacket(send.getBytes(),send.getBytes().length,
                       InetAddress.getByName("127.0.0.1"),A.PORT);
               //发送数据
               sendDS.send(datagramPacket);
          }
      }

    }
    public class ReadThread extends Thread{
       private DatagramSocket datagramSocket;

       public ReadThread(DatagramSocket datagramSocket) {
           this.datagramSocket = datagramSocket;
      }

       @Override
       public void run() {
           //不停接收数据
           while (true){
               //存储接收的数据
               byte[] bytes=new byte[1024];
               //接收的数据报
               DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length);
               try {
                   //等待接收数据
                   datagramSocket.receive(datagramPacket);
                   //获取接收的数据并转换成字符串
                   String receive=new String(datagramPacket.getData(),0,datagramPacket.getLength());
                   System.out.println("对方说:"+receive);
                   System.out.println("请输入发送的内容:");
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
      }
    }

     

     

    记得快乐
  • 相关阅读:
    Restful 的概念预览
    Bootstrap中alerts的用法
    Bootstrap HTML编码规范总结
    Bootstrap中img-circle glyphicon及js引入加载更快的存放位置
    PI数据库
    memcached
    Bootstrap中样式Jumbotron,row, table的实例应用
    js事件监听
    jquery显示隐藏操作
    HDU4521+线段树+dp
  • 原文地址:https://www.cnblogs.com/Y-wee/p/13476752.html
Copyright © 2020-2023  润新知