1. 网络编程概述

计算机网络

是指将地理位置不同的具有独立功能的多台计算机及外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
计算机网网络包含以下两种 :

  • 局域网 (在一定分范围内,多态计算机可以进行连接通信)
  • 广域网 (互联网)通过互联网把只要接入到互联网的计算机 连接起来

网络编程:

在一定的通信协议下 ,把网络中不同的计算机 可以进行数据交换(数据同喜)
网络编程也叫Socket编程,也叫套接字编程
在做网络编程编程时候,必须满意以下几个条件 :

  1. 计算机需要连接起来
  2. 需要在计算机中识别程序 (因为一台计算机中可能有很多程序,需要知道你和哪一个程序进行数据通信)
  3. 如果两台计算机中的程序进行数据交互,必须有一个规则
  4. 把计算机连接起来 ,我们可以使用 IP 来创建局域网或者互联网
  5. 使用 端口 ,进行识别一个计算机中的一个程序
  6. 数据交互的规则 ,使用通信协议

    IP、端口、通信协议 也是网络编程的三要素

    http://192.168.157.1:8888/mysite/a.jpg
    http :通信协议
    192.168.157.1 : IP地址 找到一台电脑 在网络中
    8888 :端口 找到这台电脑中的一个服务程序

    IP地址:

    家庭地址是我们每个人的家的唯一标识,那么IP地址,就是每一台电脑在网络范围内的唯一标识!
    网络编程 - 图1
    查看IP地址:ipconfig | ipconfig -all
    测试两台计算机的网络,是否通畅? ping IP地址

    端口:

    端口就相当于每个家的门!应用程序就可以通过这个门与外部进行数据交互。每一台电脑可能安装了非常多的应用程序,为了保证相互之间互不干扰,于是,这个门变成了一个应用程序对应一扇门(每个程序都对应一个对应的端口号)!
    网络编程 - 图2

    通讯协议:

    就是计算机与计算机之间进行通讯,需要遵从的规则!
    现实的协议:人类的语言,特务之间的通讯方式(03-160.5.10,02-119.6.12,04-111.10.15…… 蜗牛你好!!!)
    计算机之间,如果要通讯,一定要遵从协议!我们在这周会学:TCP/IP、UDP协议
    在我们访问一些网站的时候, 我们是用都是 http协议 ,http协议其实也是对 TCP/IP的一个封装

    2. OSI七层模型

    网络:我们使用通信设备,或线路将位于不同地理位置的计算机串起来。但是网络的出现的早期,由于OS,软件,硬件都来自于不同的生产厂商,这就导致
    我们在网络通信的过程中,可能存在不兼容的问题。国际标准化组织(ISO)提出了一个OSI (Open System Interconnection 开放系统互联) —- (计算机网络通信的标准)
    网络编程 - 图3
    帧:数据链路层传输的最小单位,将网络层传输下来的数据进行转换为数据帧(帧头,数据,帧尾)!
    各层功能定义:
    从应用层 开始介绍,整个过程以公司A和公司B的一次商业报价单发送为例子进行讲解

    <1> 应用层

    OSI参考模型中最靠近用户的一层,是为计算机用户提供应用接口,也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP等。
    实际公司A的老板就是我们所述的用户,而他要发送的商业报价单,就是应用层提供的一种网络服务,当然,老板也可以选择其他服务,比如说,发一份商业合同,发一份询价单,等等。

    <2> 表示层

    表示层提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。
    由于公司A和公司B是不同国家的公司,他们之间的商定统一用英语作为交流的语言,所以此时表示层(公司的文秘),就是将应用层的传递信息转翻译成英语。同时为了防止别的公司看到,公司A的人也会对这份报价单做一些加密的处理。这就是表示的作用,将应用层的数据转换翻译等。

    <3> 会话层

    会话层就是负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。
    会话层的同事拿到表示层的同事转换后资料,(会话层的同事类似公司的外联部),会话层的同事那里可能会掌握本公司与其他好多公司的联系方式,这里公司就是实际传递过程中的实体。他们要管理本公司与外界好多公司的联系会话。当接收到表示层的数据后,会话层将会建立并记录本次会话,他首先要找到公司B的地址信息,然后将整份资料放进信封,并写上地址和联系方式。准备将资料寄出。等到确定公司B接收到此份报价单后,此次会话就算结束了,外联部的同事就会终止此次会话。

    <4> 传输层

    传输层建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。该层向高层屏蔽了下层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户控制和设定的、可靠的数据通路。我们通常说的,TCP UDP就是在这一层。端口号既是这里的“端”。
    传输层就相当于公司中的负责快递邮件收发的人,公司自己的投递员,他们负责将上一层的要寄出的资料投递到快递公司或邮局。

    <5> 网络层

    本层通过IP寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。就是通常说的IP层。这一层就是我们经常说的IP协议层。IP协议是Internet的基础。
    网络层就相当于快递公司庞大的快递网络,全国不同的集散中心,比如说,从深圳发往北京的顺丰快递(陆运为例啊,空运好像直接就飞到北京了),首先要到顺丰的深圳集散中心,从深圳集散中心再送到武汉集散中心,从武汉集散中心再寄到北京顺义集散中心。这个每个集散中心,就相当于网络中的一个IP节点。

    <6> 数据链路层

    将比特组合成字节,再将字节组合成帧,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测。
    数据链路层又分为2个子层:逻辑链路控制子层(LLC)和媒体访问控制子层(MAC)。
    MAC子层处理CSMA/CD算法、数据出错校验、成帧等;LLC子层定义了一些字段使上次协议能共享数据链路层。 在实际使用中,LLC子层并非必需的。
    这个没找到合适的例子

    <7> 物理层

    实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。
    快递寄送过程中的交通工具,就相当于我们的物理层,例如汽车,火车,飞机,船。

    3.TCP/IP五层模型(了解)

    网络编程 - 图4

    物理层物理层由来:

    孤立的计算机之间必须完成组网物理层功能:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0

    数据链路层数据链路层由来:

    单纯的电信号0和1没有任何意义,必须规定电信号多少位一组,每组什么意思数据链路层的功能:定义了电信号的分组方式包含以太网的协议、Mac地址、广播通讯等

    网略层网络层由来:

    有了ethernet、mac地址、广播的发送方式,世界上的计算机就可以彼此通信了,问题是世界范围的互联网是由一个个彼此隔离的小的局域网组成的,那么如果所有的通信都采用以太网的广播方式,那么一台机器发送的包全世界都会收到,就不仅仅是效率低的问题了,这会是一种灾难世界大网络是由一个个彼此隔离的局域网组成,以太网包只能在局域网内发送,一个局域网是一个广播域。以太网的广播包只能在一个广播域内发送,跨广播域通信只能通过路由转发网络层功能:引入一套新的地址用来区分不同的广播域子网,这套地址即网络地址包含 IP协议、子网掩码(判断任意两个IP地址是否处在同一个子网络)、IP地址、IP报文、ARP协议(获取目标主机的mac地址) 、ICMP协议

    传输层传输层的由来:

    网络层的ip帮我们区分子网,以太网层的mac帮我们找到主机,再通过端口来标识主机上的应用程序端口即应用程序与网卡关联的编号传输层功能:建立端口到端口的通信,补充:端口范围0-65535,0-1023为系统占用端口传输层有两种协议,TCP和UDP

    应用层用层由来:

    用户使用的都是应用程序,均工作于应用层,互联网是开发的,大家都可以开发自己的应用程序,数据多种多样,必须规定好数据的组织形式。应用层功能:规定应用程序的数据格式

    4、InetAddress

    InetAddress:可以说IP地址的代表
    比如:http://www.woniuxy.com/ 如何去获得对应的IP地址
    凡是网络编程的类,统一被JDK放置在java.net目录中
    网络编程 - 图5
    public class TestInetAddress {
    public static void main(String[] args) throws UnknownHostException {
    String ip = “192.168.157.1”;

    //创建这个IP地址的对象
    InetAddress netIp = InetAddress.getByName(ip);
    System.out.println(netIp);

    //获取计算机名字
    String hostName = netIp.getHostName();
    //获取IP
    String hostAddress = netIp.getHostAddress();

    System.out.println(hostName + “—-“+hostAddress);

    String url = “http://www.baidu.com“;

    //这个不能获取 到 因为 url 不是一个IP 是一个URL地址
    //InetAddress address = InetAddress.getByName(url);
    //System.out.println(address);
    }
    }

    5.url

    唯一的定义一个资源 ,
    http://192.168.157.1:8888/mysite/a.jpg
    此url地址可以定位网络中的一张图片
    https://img30.360buyimg.com/sku/jfs/t1/178877/27/18846/726252/6114dfceEa88e86c6/5f82918c16433c5a.jpg
    这个url地址唯一定位到互联网中的一张图片
    URL全称是Uniform Resource Location,也就是统一资源位置。实际上,URL就是一种特殊的URI,它除了标识一个资源,还会为资源提供一个特定的网络位置,客户端可以通过它来获取URL对应的资源。有时也被俗称为网页地址。表示为互联网上的资源,如网页或者FTP地址。
    public class TestUrl {
    public static void main(String[] args) throws IOException {
    String str = “https://img30.360buyimg.com/sku/jfs/t1/178877/27/18846/726252/6114dfceEa88e86c6/5f82918c16433c5a.jpg“;

    //创建一个URL地址对象
    URL url = new URL(str);

    //创建一个链接对象 链接到这个url地址上去
    URLConnection uc = url.openConnection();

    //获取一个输入流指向这个 图片地址i
    InputStream in = uc.getInputStream();

    File f = new File(“E:/log/wugd.jpg”);
    //创建一个输出流 把下载的图片写到我们的磁盘中
    OutputStream out = new FileOutputStream(f);

    //作业 :使用 两种读数据的方式 下载图片
    int i = in.read();
    while(i != -1){
    out.write(i);
    i = in.read();
    }

    in.close();
    out.close();
    }
    }
    http和https之间的区别 :
    他们底层都是TCP/IP协议 ,http协议没有进行加密 ,https协议在数据传输时 进行了加密,比较安全

    6、TCP

    为了建立网络中的连接的一种协议.
    TCP这是传输层的一种协议,这种协议的特点:依赖连接,数据通讯采用的1问1答模式,数据通讯安全有效,不需要担心数据可能在通讯的过程中有丢失的情况!
    TCP协议,有的人就将这个协议也称为:TCP/IP协议

    TCP的三次握手

    TCP在创建连接的时候,需要经过三次握手,才能建立连接:
    网络编程 - 图6
    第一次握手(**客户端发送请求):建立连接时,客户端发送SYN包(SYN=i)到服务器,并进入到SYN-SEND状态,等待服务器确认
    男孩:“咱俩处对象吧”,并等待对方回复确认。
    第二次握手(**服务端回传确认):服务器收到SYN包,必须确认客户的SYN(ack=i+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN-RECV状态
    女孩:告诉男孩说“可以啊”
    第三次握手(**客户端回传确认)**:客户端收到服务器的SYN+ACK包,向服务器发送确认报ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手,客户端与服务器开始传送数据。
    男孩说:“那就在一块吧”,于是俩人有了情侣链接关系了。
    完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等

    断开连接的四次挥手

    如果客户端和服务器端断开连接 ,需要四次挥手 :
    网络编程 - 图7
    由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
    第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
    女孩:要和男孩分手,并等待男孩回应
    第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
    男孩:回复女孩,先同意分手,不过还是又考虑了一段时间
    第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
    男孩思考了一段时间,还是选择了分手
    第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
    女孩收到消息,于是俩人正式分手
    总结 :

  7. 第一步:客户端 发送请求,断开连接 (客户端发送)

  8. 针对请求,服务器发起响应,确定收到断开请求 (服务器端发送)
  9. 服务器向客户端发送请求,请求断开连接(服务器端发送)
  10. 客户端确认断开(客户端发送)

    思考(面试题):

    为什么连接的时候是三次握手,关闭的时候却是四次挥手?
    因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。其中ACK报文是用来应答的,SYN报文是用来同步的。
    而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,服务器方也未必将全部数据都发送给对方了,所以服务器方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
    利用TCP协议做网络编程的开发:

  11. 案例一 (最简单的)

    • 服务端开发public class TestServer {
      public static void main(String[] args) throws IOException {
      //创建一个服务器套接字
      ServerSocket ss = new ServerSocket(9999);
      System.out.println(“服务器启动…”);

      //等待客户端的连接 使用到了一个阻塞方法
      Socket client = ss.accept();
      System.out.println(“客户端链接上来了。。。”);
      }
      }
    • 客户端开发public class TestClient {
      public static void main(String[] args) throws UnknownHostException, IOException {
      //localhost = 127.0.0.1 代表本机
      Socket client = new Socket(“localhost”, 9999);
      System.out.println(“客户端启动”);
      }
      }先启动服务器端 在启动客户端
    1. 案例2客户端可以向服务器端发送信息 ,服务器端也可以向客户端发送信息,也都可以接收信息:服务器端 :public class TestSerever {
      public static void main(String[] args) throws IOException {
      //创建一个服务器套接字 端口 一般建议 8000以上
      //一些比较小的端口 都被一些服务占用了
      ServerSocket ss = new ServerSocket(9999);
      System.out.println(“服务器启动…”);

      //等待客户端的连接 使用到了一个阻塞方法
      Socket client = ss.accept();
      System.out.println(“客户端链接上来了。。。”);

      //接收 客户端信息 需要输入流 读信息
      InputStream inputStream = client.getInputStream();
      DataInputStream dis = new DataInputStream(inputStream);
      String str = dis.readUTF();
      System.out.println(str);

      //从服务器端也发送一个信息呢 写信息
      OutputStream outputStream = client.getOutputStream();
      DataOutputStream dos = new DataOutputStream(outputStream);
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      System.out.println(“请输入信息:”);
      String s = br.readLine();
      dos.writeUTF(s);

      dis.close();
      inputStream.close();
      dos.close();
      outputStream.close();
      }
      }客户端 :public class TestClient {
      public static void main(String[] args) throws UnknownHostException, IOException {
      //localhost = 127.0.0.1 = 192.168.157.1代表本机
      Socket client = new Socket(“localhost”, 9999);
      System.out.println(“客户端启动”);

      //发送信息 到服务器端
      //获取一个输出流
      OutputStream outputStream = client.getOutputStream();
      //因为字节流输出数据不方便 尤其是字符串 写信息
      DataOutputStream dos = new DataOutputStream(outputStream);
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      System.out.println(“请输入信息:”);
      String s = br.readLine();
      dos.writeUTF(s);

      //读信息
      InputStream inputStream = client.getInputStream();
      DataInputStream dis = new DataInputStream(inputStream);
      String str = dis.readUTF();
      System.out.println(str);

      dos.close();
      outputStream.close();
      dis.close();
      inputStream.close();
      }
      }
    2. 案例3 目前程序不能连续的发信息 ,发一次 接一次 ,服务器就停了 这样肯定不行服务端 :public class TestSerever {
      public static void main(String[] args) throws IOException {
      //创建一个服务器套接字 端口 一般建议 8000以上
      //一些比较小的端口 都被一些服务占用了
      ServerSocket ss = new ServerSocket(9999);
      System.out.println(“服务器启动…”);

      //等待客户端的连接 使用到了一个阻塞方法
      Socket client = ss.accept();
      System.out.println(“客户端链接上来了。。。”);


      while(true){
      //接收 客户端信息 需要输入流 读信息
      InputStream inputStream = client.getInputStream();
      DataInputStream dis = new DataInputStream(inputStream);
      String str = dis.readUTF();
      System.out.println(str);

      //从服务器端也发送一个信息呢 写信息
      OutputStream outputStream = client.getOutputStream();
      DataOutputStream dos = new DataOutputStream(outputStream);
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      System.out.println(“请输入信息:”);
      String s = br.readLine();
      dos.writeUTF(s);
      }


      /dis.close();
      inputStream.close();
      dos.close();
      outputStream.close();
      /
      }
      }客户端 :public class TestClient {
      public static void main(String[] args) throws UnknownHostException, IOException {
      //localhost = 127.0.0.1 = 192.168.157.1代表本机
      Socket client = new Socket(“localhost”, 9999);
      System.out.println(“客户端启动”);


      while(true){
      //发送信息 到服务器端
      //获取一个输出流
      OutputStream outputStream = client.getOutputStream();
      //因为字节流输出数据不方便 尤其是字符串 写信息
      DataOutputStream dos = new DataOutputStream(outputStream);
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      System.out.println(“请输入信息:”);
      String s = br.readLine();
      dos.writeUTF(s);

      //读信息
      InputStream inputStream = client.getInputStream();
      DataInputStream dis = new DataInputStream(inputStream);
      String str = dis.readUTF();
      System.out.println(str);
      }

      /dos.close();
      outputStream.close();
      dis.close();
      inputStream.close();
      /
      }
      }
    3. 案例4目前程序可以连续的发信息 和接收信息 ,但是不能链接多个客户端 ,如果说我们想连接多个客户端,这时就需要考虑多线程了,只要有客户端链接 服务器就开启一个线程 去接待这个客户端服务端 :public class TestSerever {
      public static void main(String[] args) throws IOException {
      //创建一个服务器套接字 端口 一般建议 8000以上
      //一些比较小的端口 都被一些服务占用了
      ServerSocket ss = new ServerSocket(9999);
      System.out.println(“服务器启动…”);

      while(true){
      //等待客户端的连接 使用到了一个阻塞方法
      Socket client = ss.accept();
      System.out.println(“客户端链接上来了。。。”);

      Runnable r = ()->{
      //把这个客户端 交给这个线程进行处理
      Socket socket = client; //

      while(true){
      try{
      //接收 客户端信息 需要输入流 读信息
      InputStream inputStream = socket.getInputStream();
      DataInputStream dis = new DataInputStream(inputStream);
      String str = dis.readUTF();
      System.out.println(str);

      //是不是可以接收到的所有的客户端信息 发送到一个文件中保存

      //从服务器端也发送一个信息呢 写信息
      OutputStream outputStream = socket.getOutputStream();
      DataOutputStream dos = new DataOutputStream(outputStream);
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      System.out.println(“请输入信息:”);
      String s = br.readLine();
      dos.writeUTF(s);
      }catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
      }
      }
      } ;

      //启动线程
      Thread t = new Thread(r);
      t.start();
      }
      }
      }客户端 :public class TestClient {
      public static void main(String[] args) throws UnknownHostException, IOException {

      Runnable r = ()->{
      try{
      //localhost = 127.0.0.1 = 192.168.157.1代表本机
      Socket client = new Socket(“localhost”, 9999);
      System.out.println(“客户端启动”);

      while(true){
      //发送信息 到服务器端
      //获取一个输出流
      OutputStream outputStream = client.getOutputStream();
      //因为字节流输出数据不方便 尤其是字符串 写信息
      DataOutputStream dos = new DataOutputStream(outputStream);
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      System.out.println(“请输入信息:”);
      String s = br.readLine();
      dos.writeUTF(s);

      //读信息
      InputStream inputStream = client.getInputStream();
      DataInputStream dis = new DataInputStream(inputStream);
      String str = dis.readUTF();
      System.out.println(str);
      }
      }catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
      }
      };

      Thread t = new Thread(r);
      t.start();

      /dos.close();
      outputStream.close();
      dis.close();
      inputStream.close();
      /
      }
      }