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();
          }
      }
  }
}

 

 

记得快乐
原文地址:https://www.cnblogs.com/Y-wee/p/13476752.html