1. 网络编程基本概念
    • 什么是计算机网络

      • 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源
    • 计算机网络的主要功能

      • 资源共享
      • 信息传输与集中处理
      • 均衡负荷与分布处理
      • 综合信息服务(www/综合业务数字网络ISDN)等
    • 网络通信协议

      • 要使计算机连成的网络能够互通信息,需要对数据输出速率、传输代码、代码结构、传输控制步骤、出错控制等指定一组标准,这一组共同遵守的通信标准及时网络通信协议,不同的计算机之间必须使用相同的通讯协议才能进行通信
    • 网络通信接口

      • 为了使两个结点之间能进行对话,必须在它们之间建立通信工具(即接口),使彼此之间能进行信息交换。接口包括两部分:
        • 硬件装置:实现结点之间的信息传送
        • 软件装置:规定双方进行通信的约定协议
    • TCP/IP

      • 传输控制协议/因特网互联协议,又叫网络通信协议,这个协议是Internet最基本的协议、Internet国际互联网络的基础,简单地说,就是由网络层的IP协议和传输层的TCP协议组成的
      • IP地址:网络中每台计算机的一个标识号,本地IP:127.0.0.1 localhost
        • 端口号(PORT):端口号的范围:0~65535之间,0~1023之间的端口数是用于一些知名的网络服务和
    • 网络模型

      [外链图片转存失败(img-BUDU8wAy-1568346005676)(D:\Desktop\OSI_TCPIP.jpg)]

    • 程序开发结构

      • 网络编程主要是指完成C/S程序的开发,程序的开发结构有两种:
        • C/S(客户端/服务器):开发两套程序,两套程序需要同时维护。C/S程序一般比较稳定
        • B/S(浏览器/服务器):开发一套程序,客户端使用浏览器进行访问。B/S程序一般稳定性很差,而且安全性较差
      • C/S程序主要可以完成以下两种程序的开发:
        • TCP(Transmission Control Protocol):传输控制协议,采用三次握手的方式,保证准确的连接操作
        • UDP(User Datagram Protocol):数据报协议,发送数据报
  2. 网络编程TCP协议
    • TCP程序概述

      • TCP是一个可靠的协议,面向连接的协议
      • 实现TCP协议,需要编写服务器端和客户端,Java API为我们提供了java.net包,为实现网络应用程序提供类
      • ServerSocket:此类实现服务器套接字
      • Socket:此类实现客户端套接字
      • Socket是网络驱动层提供给应用程序编程的接口和一种机制
    • 实现服务器端与客户端

      • 服务器端

        // 此类实现服务器套接字。服务器套接字等待请求通过网络接入。它基于该请求执行某些操作,然后可能向请求者返回结果
        public class ServerSocket extends Object// 创建绑定到特定端口的服务器套接字
        ServerSocket(int port);
        // 通过指定超时值启动/禁用SO_TIMEOUT,以毫秒为单位
        void setSoTimeout(int timeout);
        // 返回此服务器套接字的本地地址
        InetAddress getInetAddress();
        // 侦听并接受到此套接字的连接
        Socket accept();
        
      • 客户端

        // 此类实现客户端套接字(也可以就叫做“套接字”)。套接字是两台机器间通信的端点
        public class Socket extends Object;
        // 创建一个流套接字并将其连接到指定主机上的指定端口号
        Socket(String host, int port);
        // 返回此套接字的输入流
        InputStream getInputStream();
        // 返回此套接字的输出流
        OutputStream getOutputStream();
        // 启用/禁用带有指定超时值的SO_TIMEOUT,以毫秒为单位
        void setSoTimeout(int timeout);
        
  3. TCP实现ECHO程序
    • Echo,意为应答,程序的功能是客户端向服务器发送一个字符串,服务器不做任何处理,直接把字符串返回给客户端,Echo程序是最基本的客户/服务器程序

    • 示例:

      // EchoServerDemo
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /** * @author xiao儿 * @date 2019/9/12 9:19 * @Description EchoServerDemo */
      public class EchoServerDemo {
          public static void main(String[] args) {
              try {
                  // 创建一个服务器端的Socket(2014~65535)
                  ServerSocket server = new ServerSocket(6666);
                  System.out.println("服务器已启动,正在等待客户端连接...");
                  // 等待客户端的连接,造成阻塞,如果有客户端连接成功,立即返回一个Socket对象
                  Socket accept = server.accept();
                  System.out.println("客户端连接成功" + accept.getInetAddress().getHostAddress());
                  BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
                  // 通过输入流来读取网络数据,如果没有消息,会造成阻塞
                  String info = br.readLine();
                  System.out.println(info);
                  // 获取输出流,向客户端返回消息
                  PrintStream ps = new PrintStream(new BufferedOutputStream(accept.getOutputStream()));
                  ps.println("echo:" + info);
                  ps.flush();
                  // 关闭流
                  ps.close();
                  br.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
      // EchoClientDemo
      import java.io.*;
      import java.net.Socket;
      
      /** * @author xiao儿 * @date 2019/9/12 9:25 * @Description EchoClientDemo */
      public class EchoClientDemo {
          public static void main(String[] args) {
              try {
                  // 创建一个Socket对象,指定要连接的服务器
                  Socket socket = new Socket("localhost", 6666);
                  // 获取socket输入输出流
                  PrintStream ps = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
                  BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                  ps.println("hello,my name is Tom");
                  ps.flush();
                  // 读取服务器端返回的数
                  String info = br.readLine();
                  System.out.println(info);
                  ps.close();
                  br.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
  4. 服务器和多客户端通信
    • 服务器同时支持多个客户端的连接,就必须加入多线程的处理机制,将每一个连接的客户端都创建一个线程对象

    • 示例:

      // MutilServerDemo
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      /** * @author xiao儿 * @date 2019/9/12 9:49 * @Description MutilServerDemo * * 处理多个客户端 * 主线程用于监听客户端的连接,每次有连接成功时开启一个线程来处理该客户端的消息 */
      public class MutilServerDemo {
          public static void main(String[] args) {
              // 开启线程池
              ExecutorService es = Executors.newFixedThreadPool(3);
              try {
                  ServerSocket server = new ServerSocket(6666);
                  System.out.println("服务器已启动,正在等待连接...");
                  while (true) {
                      Socket accept = server.accept();
                      System.out.println(accept.getInetAddress().getHostAddress());
                      es.execute(new UserThread(accept));
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
      /** * 用来处理客户端请求的线程任务 */
      class UserThread implements Runnable {
          private Socket socket;
      
          public UserThread(Socket socket) {
              this.socket = socket;
          }
      
          @Override
          public void run() {
              try {
                  BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                  PrintStream ps = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
                  String info = br.readLine();
                  System.out.println(info);
                  ps.println("echo:" + info);
                  ps.flush();
                  ps.close();
                  br.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
  5. 多客户端之间的通信
    • 客户端的数据包通过服务器中转,发送到另一个客户端

    • 示例:

      // MessageType
      /** * @author xiao儿 * @date 2019/9/12 10:44 * @Description MessageType */
      public class MessageType {
          public static final int TYPE_LOGIN = 0x1;// 登录消息的内容
          public static final int TYPE_SEND = 0x2;// 发送消息的类型
      }
      
      // Message
      import java.io.Serializable;
      
      /** * @author xiao儿 * @date 2019/9/12 10:42 * @Description Message * * 消息包 */
      public class Message implements Serializable {
          private String from;// 发送者
          private String to;// 接收者
          private int type;// 消息类型
          private String info;// 消息
      
          public String getFrom() {
              return from;
          }
      
          public void setFrom(String from) {
              this.from = from;
          }
      
          public String getTo() {
              return to;
          }
      
          public void setTo(String to) {
              this.to = to;
          }
      
          public int getType() {
              return type;
          }
      
          public void setType(int type) {
              this.type = type;
          }
      
          public String getInfo() {
              return info;
          }
      
          public void setInfo(String info) {
              this.info = info;
          }
      
          public Message() {
          }
      
          public Message(String from, String to, int type, String info) {
              this.from = from;
              this.to = to;
              this.type = type;
              this.info = info;
          }
      }
      
      // Server
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.Vector;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      /** * @author xiao儿 * @date 2019/9/12 10:15 * @Description Server * <p> * 服务器端 */
      public class Server {
          public static void main(String[] args) {
              // 保存客户端处理的线程
              Vector<UserThread> vector = new Vector<>();
              ExecutorService es = Executors.newFixedThreadPool(5);
              try {
                  // 创建服务器端的Socket
                  ServerSocket server = new ServerSocket(8888);
                  System.out.println("服务器已启动,正在等待连接...");
                  while (true) {
                      Socket socket = server.accept();
                      UserThread userThread = new UserThread(socket, vector);
                      es.execute(userThread);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
      /** * 客户端处理的线程 */
      class UserThread implements Runnable {
          private String name;// 客户端的用户名称(唯一)
          private Socket socket;
          private Vector<UserThread> vector;// 客户端处理线程的集合
          private ObjectInputStream ois;
          private ObjectOutputStream oos;
          private boolean flag = true;
      
          public UserThread(Socket socket, Vector<UserThread> vector) {
              this.socket = socket;
              this.vector = vector;
              vector.add(this);
          }
      
          @Override
          public void run() {
              try {
                  System.out.println("客户端:" + socket.getInetAddress().getHostAddress() + "已连接");
                  ois = new ObjectInputStream(socket.getInputStream());
                  oos = new ObjectOutputStream(socket.getOutputStream());
                  while (flag) {
                      // 读取消息对象
                      Message message = (Message) ois.readObject();
                      int type = message.getType();
                      switch (type) {
                          case MessageType.TYPE_SEND:
                              String to = message.getTo();
                              UserThread ut;
                              int size = vector.size();
                              for (int i = 0; i < size; i++) {
                                  ut = vector.get(i);
                                  if (to.equals(ut.name) && ut != this) {
                                      ut.oos.writeObject(message);
                                      break;
                                  }
                              }
                              break;
                          case MessageType.TYPE_LOGIN:
                              name = message.getFrom();
                              message.setInfo("欢迎你:");
                              oos.writeObject(message);
                              break;
                      }
                  }
                  ois.close();
                  oos.close();
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
          }
      }
      
      // Client
      import java.io.*;
      import java.net.Socket;
      import java.util.Scanner;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      /** * @author xiao儿 * @date 2019/9/12 10:16 * @Description Client */
      public class Client {
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
              ExecutorService es = Executors.newSingleThreadExecutor();
              try {
                  Socket socket = new Socket("localhost", 8888);
                  System.out.println("服务器连接成功");
                  ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                  ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                  // 向服务器发送登录消息
                  System.out.println("请输入名称:");
                  String name = sc.nextLine();
                  Message message = new Message(name, null, MessageType.TYPE_LOGIN, null);
                  oos.writeObject(message);
                  message = (Message) ois.readObject();
                  System.out.println(message.getInfo() + message.getFrom());
                  // 启动读取消息的线程
                  es.execute(new ReadInfoThread(ois));
                  // 使用主线程来实现发送消息
                  boolean flag = true;
                  while (flag) {
                      message = new Message();
                      System.out.println("To:");
                      message.setTo(sc.nextLine());
                      message.setFrom(name);
                      message.setType(MessageType.TYPE_SEND);
                      System.out.println("Info:");
                      message.setInfo(sc.nextLine());
                      oos.writeObject(message);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
          }
      }
      
      /** * 读取消息的线程 */
      class ReadInfoThread implements Runnable {
          private ObjectInputStream in;
          private boolean flag = true;
      
          public ReadInfoThread(ObjectInputStream in) {
              this.in = in;
          }
      
          public void setFlag(boolean flag) {
              this.flag = flag;
          }
      
          @Override
          public void run() {
              try {
                  while (flag) {
                      Message message = (Message) in.readObject();
                      System.out.println("[" + message.getFrom() + "]对我说:" + message.getInfo());
                  }
                  if (in != null) {
                      in.close();
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
          }
      }
      
  6. 网络编程UDP协议
    • UDP协议概述

      • UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传目的地,因此是否能到大目的地,到达目的地的时间以及内容的正确性都是不能被保证的,每个传输的数据报必须限定在64KB之内
      • 主要使用以下两个类:
        • DatagramPacket:此类表示数据包
        • DatagramSocket:此类表示用来发送和接收数据报包的套接字
    • 示例:

      // UDPServerDemo
      import java.io.IOException;
      import java.net.*;
      
      /** * @author xiao儿 * @date 2019/9/12 15:10 * @Description UDPServerDemo */
      public class UDPServerDemo {
          public static void main(String[] args) {
              String info = "good good 学习,天天 up";
              byte[] bytes = info.getBytes();
              try {
                  // 封装一个数据报包
                  DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("127.0.0.1"), 8000);
                  // 本程序的端口
                  DatagramSocket socket = new DatagramSocket(9000);
                  socket.send(dp);
                  socket.close();
              } catch (UnknownHostException e) {
                  e.printStackTrace();
              } catch (SocketException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
      // UDPClientDemo
      import java.io.IOException;
      import java.net.DatagramPacket;
      import java.net.DatagramSocket;
      import java.net.SocketException;
      
      /** * @author xiao儿 * @date 2019/9/12 15:18 * @Description UDPClientDemo * * 客户端 */
      public class UDPClientDemo {
          public static void main(String[] args) {
              byte[] bytes = new byte[1024];
              DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
              try {
                  DatagramSocket socket = new DatagramSocket(8000);
                  System.out.println("正在接收数据中...");
                  socket.receive(dp);// 接收数据
                  String s = new String(dp.getData(), 0, dp.getLength());
                  System.out.println(s);
                  socket.close();
              } catch (SocketException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
  7. URL
    • URL概述

      • URL(uniform resource location)类URL代表一个统一资源定位符,它是指向互联网“资源”的指针。
      • 抽象类URLConnectio是所有类的超类,它代表应用程序和URL之间的通信链接
    • 示例:

      import java.io.BufferedInputStream;
      import java.io.BufferedOutputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.net.HttpURLConnection;
      import java.net.MalformedURLException;
      import java.net.URL;
      
      /** * @author xiao儿 * @date 2019/9/12 16:10 * @Description URLDemo */
      public class URLDemo {
          public static void main(String[] args) {
              try {
                  URL url = new URL("http://pic.netbian.com/uploads/allimg/190905/231431-15676964715fb0.jpg");
                  HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                  BufferedInputStream in = new BufferedInputStream(httpURLConnection.getInputStream());
                  BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("./Java入门/src/day10_网络编程/url/photo.jpg"));
                  byte[] bytes = new byte[1024];
                  int len = -1;
                  while ((len = in.read(bytes)) != -1) {
                      out.write(bytes, 0, len);
                      out.flush();
                  }
                  in.close();
                  out.close();
                  System.out.println("下载成功");
              } catch (MalformedURLException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
  8. MINA框架
    • MINA是一个简洁易用的基于TCP/IP通信的MINA框架

    • 下载地址:http://mina.apache.org/downloads-mina_2_0.html

    • 一个简单的网络程序需要的最少jar包:mina-core-2.0.16.jar、slf4j-api-1.7.21.jar

    • 开发一个MINA应用,简单的说,就是创建连接,设定过滤规则,编写自己的消息处理器

    • 示例:

      // 服务器端
      import org.apache.mina.core.service.IoHandlerAdapter;
      import org.apache.mina.core.session.IoSession;
      
      /** * @author xiao儿 * @date 2019/9/12 16:41 * @Description MinaServerHandler * * 服务器端的消息处理器 */
      public class MinaServerHandler extends IoHandlerAdapter {
          // 一次会话被打开
          @Override
          public void sessionOpened(IoSession session) throws Exception {
              super.sessionOpened(session);
              System.out.println("Welcome client :" + session.getRemoteAddress());
          }
      
          // 会话关闭
          @Override
          public void sessionClosed(IoSession session) throws Exception {
              super.sessionClosed(session);
              System.out.println("client closed");
          }
      
          // 接收消息
          @Override
          public void messageReceived(IoSession session, Object message) throws Exception {
              super.messageReceived(session, message);
              String msg = (String) message;// 接收到的消息对象
              System.out.println("收到客户端发来的消息:" + msg);
              // 向客户端发送消息对象
              session.write("echo:" + msg);
          }
      }
      
      import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
      import org.apache.mina.filter.codec.ProtocolCodecFilter;
      import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
      import org.apache.mina.transport.socket.SocketAcceptor;
      import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
      
      import java.io.IOException;
      import java.net.InetSocketAddress;
      
      /** * @author xiao儿 * @date 2019/9/12 16:35 * @Description Server */
      public class Server {
          public static void main(String[] args) {
              // 创建一个非阻塞的Server端Socket NIO
              SocketAcceptor acceptor = new NioSocketAcceptor();
              DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
              chain.addLast( "myChain", new ProtocolCodecFilter(new TextLineCodecFactory()));
              // 设置服务器端的消息处理器
              acceptor.setHandler(new MinaServerHandler());
              int port = 9999;// 服务器的端口号
              try {
                  // 绑定端口号,并启动服务器(不会阻塞,立即返回)
                  acceptor.bind(new InetSocketAddress(port));
              } catch (IOException e) {
                  e.printStackTrace();
              }
              System.out.println("Mina Server running, linstener on :" + port);
          }
      }
      
      // 客户端
      import org.apache.mina.core.service.IoHandlerAdapter;
      import org.apache.mina.core.session.IoSession;
      
      /** * @author xiao儿 * @date 2019/9/13 10:01 * @Description MinaClientHandler */
      public class MinaClientHandler extends IoHandlerAdapter {
          @Override
          public void sessionOpened(IoSession session) throws Exception {
              super.sessionOpened(session);
              System.out.println("sessionOpened");
          }
      
          @Override
          public void sessionClosed(IoSession session) throws Exception {
              super.sessionClosed(session);
              System.out.println("sessionClosed");
          }
      
          @Override
          public void messageReceived(IoSession session, Object message) throws Exception {
              super.messageReceived(session, message);
              String msg = (String) message;
              System.out.println(msg);
          }
      }
      
      import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
      import org.apache.mina.core.future.ConnectFuture;
      import org.apache.mina.filter.codec.ProtocolCodecFilter;
      import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
      import org.apache.mina.transport.socket.nio.NioSocketConnector;
      
      import java.net.InetSocketAddress;
      import java.util.Scanner;
      
      /** * @author xiao儿 * @date 2019/9/13 10:01 * @Description Client */
      public class Client {
          public static void main(String[] args) {
              // 创建连接
              NioSocketConnector connector = new NioSocketConnector();
              DefaultIoFilterChainBuilder chain = connector.getFilterChain();
              chain.addLast("myChain", new ProtocolCodecFilter(new TextLineCodecFactory()));
              connector.setHandler(new MinaClientHandler());
              connector.setConnectTimeoutMillis(10000);
              // 连接服务器
              ConnectFuture cf = connector.connect(new InetSocketAddress("localhost", 9999));
              cf.awaitUninterruptibly();// 等待连接成功
              Scanner input = new Scanner(System.in);
              while (true) {
                  System.out.println("请输入:");
                  String info = input.nextLine();
                  // 发送消息
                  cf.getSession().write(info);
              }
              // 等待服务器连接关闭,结束长连接
              // cf.getSession().getCloseFuture().awaitUninterruptibly();
              // connector.dispose();// 关闭连接
          }
      }