20.网络编程

  1. 什么是网络编程
    1. 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据
    2. 程序员所做的事情就是把数据发送到指定的位置,或者接收到指定的数据
    3. 在接收和发送数据时,大部分程序设计语言都设计了专门的API实现之些功能,程序员只需要调用即可
      1. 所以基础的网络编程可以和打电话一样简单
  2. 网络编程中的几个概念
    1. IP地址
      1. 网络上的每个设备,网络中每个设备都会有一个唯一的数字标识即是IP地址,就像手机号码
      2. IPv4:4 个字节组成,4 个0-255。大概42 亿,30 亿都在北美,亚洲4 亿。2011年初已经用尽
      3. IPv6:8 组,每组4 个16 进制数
    2. 域名
      1. IP地址不便于记忆(4个字节组成数字),域名相当于它的别名:类比手机中的通讯簿
      2. 一个域名只能对应一个ip地址,可以多个域名映射同一个ip地址
      3. DNS服务器(域名解析):用户在浏览器输入域名时,浏览器先请求DNS服务器,将域名转换成IP地址,然后将转换后的IP地址反馈给浏览器,然后进行实际的数据传输
    3. 端口
      1. 访问不同的程序就需要相应的端口号
      2. 硬件上规定,端口号码必须位于0-65535之间,每个端口对应一个网络程序,一个网络程序可使用多个端口
      3. 不要用1024以下的端口,因为多半被系统占用
      4. 常用端口
        1. mysql: 3306
        2. oracle: 1521
        3. web: 80
        4. tomcat: 8080
        5. QQ: 4000
    4. 协议(protocol)
      1. 数据的格式(即为协议),为了让接收端理解传输的数据
      2. 开发常识
        1. 最麻烦的内容不是数据的发送和接收,因为这个功能几乎在所有的程序语言中都提供了封装好的API调用
        2. 最麻烦的是协议的设计以及协议的生产和解析,这才是网络编程的核心内容
      3. 扩展了解
        1. 如何编写这种协议呢
          1. 答案是随意的,但是要如下要求
            1. 按照这种协议格式能生成唯一的编码
            2. 根据该编码可唯一解析发送数据的内容
            3. 由于各个网络程序之间协议格式不同,所以导致客户端程序者是专用的结构
    5. 服务器与客户端之间的关系,举例说明
      1. 呼叫中心10086,一个用户拨打10086,转接给一个业务人员处理问题,另一个用户拨打1006,转接另一个客服人员,处理问题
        1. 每个用户相当于一个客户端程序
        2. 呼叫中心即是服务端程序
        3. 10086相当于端口号
        4. 每个客服人员相当于服务端专门启动和客户端连接的线程
      2. 通常上客户端对服务端是n对一的关系,
  3. 网络编程的总体流程
    1. 通过IP地址及端口号查找到计算机的特点程序,然后发送接收数据即可
  4. 网络编程的实现思路
    1. 实现模型:请求-响应模型
      1. 1.通讯一端发送数据,另外一端反馈数据
      2. 第一次主动发送数据的程序被称为客户端程序(客户端)
      3. 在第一次通讯中等待连接的程序被称为服务端程序(server)
      4. 一旦建立连接,则客户端与服务顺完全一样,没有本质区别
      5. 测试时需要先启动服务端程序,后启动客户端程序
    2. 架构模式
      1. cs架构
        1. 举例:如,qq:每个qq用户安装的都是qq客户端程序,qq服务器端程序运行在腾讯公司中,为大量qq用户提供服务
        2. 优缺点:客户端与服务端专门开发:表现丰富
      2. bs架构
        1. 举例:如web网站:不需要开发客户端应用程序,只需要写好服务器端程序即可
        2. 优缺点:便于维护,表现能力受浏览器厂商,浏览器版本影响
    3. 通讯方式
      1. TCP(传输控制协议)
        1. 举例:拨打电话就好比使用的TCP协议,它有如下特点
          1. 拨打电话方式可以保证将信息传递给别人,接听电话本身就是确认接收到信息
          2. 需要建立虚拟连接,对服务器压力较大,然后进行可靠的数据传输,如果数据发送失败,客户端自动重拨
          3. 重要的数据使用tcp,速度稍微慢些,数据量大些
      2. UDP(用户数据报协议)
        1. 举例:发送短信好比使用的UDP协议,它有如下特点
          1. 用户不一定能接收到
          2. 不需要建立具虚拟连接,,对服务器的压力小,传输不可靠,发送失败,客户端无法获得
          3. 不重要的数据使用UDP,速度快,信息量小
    4. 编码思路(无论是何种语言,何种方式进行基础的网络编程,如下步骤都是固定的:因为编程的基础功能都已经由API实现了)
      1. 1.BS结构的编程只需要实现服务端即可(客户端已经实现了):如监听页面某对象状态是否发生改变,然后采取相应的操作
      2. cs架构的编程,既需要实现服务端也需要实现客户端
      3. 客户端编程的步骤
        1. 建立网络连接(建立虚拟连接后,后续操作才能继续)
          连接到指定IP地址和端口号的服务器
        2. 交换数据(按照请求响应模型:先发送后接收)
          客户端发送请求数据,服务器根据请求响应数据
        3. 关闭网络连接,释放资源(占用的内存,占用的端口)
      4. 服务端编程的步骤
        1. 监听端口(开放给客户端的端口),获得连接
          当客户端连接到服务器时,服务器就获得一个连接,这个连接包含客户端的信息(IP地址),它们通过此连接交换数据
        2. 交换数据(按照请求响应模型:先接收再发送)
          首先接收客户端发送过来的数据,然后逻辑处理,把处理以后的结果数据发送给客户端
        3. 关闭网络连接,释放资源(占用的内存,占用的端口)
  5. Java网络编程
    1. API有哪些
      1. java.net.InetAddress:此类表示互联网协议 (IP) 地址
        成员方法
        String getHostName() 获取此 IP 地址的主机名。
        String getHostAddress() 返回 IP 地址字符串(以文本表现形式)
        static InetAddress getLocalHost() 返回本地主机。
        static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
        boolean isReachable(int timeout) 测试是否可以达到该地址(实现尽最大努力试图到达主机,但防火墙和服务器配置可能阻塞请求,使其在某些特定的端口可以访问时处于不可到达状态)
        端口是否打开的根本是取决于你开的服务或者是应用,这些才会去打开原本没有打开的端口防火墙只不过在开启的时候会对这些端口做防护而已,并不是防火墙开的这些端口。
        比如80端口,你只有做了web应用,如iis等服务器上才会打开80端口,这时候防火墙可能会保护80端口,使外面的用户无法访问,但关闭了防火墙,防护取消就可以正常访问了,所以防火墙并不是打开80的根本
        阿里云个傻叉,把80端口占用了,linux,的话使用sudo fuser -k 80/tcp命令停止阿里云的占用进程
      2. java.net.DatagramPacket:此类表示数据报包
        构造方法
        DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包
        DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号
        成员方法(length,address,port的setter/getter方法;socketAddress保护(address,port))
        void setLength(int length) 为此包设置长度
        void setAddress(InetAddress iaddr) 设置要将此数据报发往的那台机器的 IP 地址
        void setPort(int iport) 设置要将此数据报发往的远程主机上的端口号
        int getLength() 返回将要发送或接收到的数据的长度
        InetAddress getAddress() 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
        int getPort() 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的
      3. java.net.DatagramSocket:此类表示用来发送和接收数据报包的套接字,数据报套接字是包投递服务的发送或接收点
        构造方法
        DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口
        DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。
        DatagramSocket(SocketAddress bindaddr) 创建数据报套接字,将其绑定到指定的本地套接字地址
        成员方法
        void close() 关闭此数据报套接字
        InetAddress getInetAddress() 返回此套接字连接的地址
        InetAddress getLocalAddress() 获取套接字绑定的本地地址
        int getLocalPort() 返回此套接字绑定的本地主机上的端口号
        int getPort() 返回此套接字的端口
        boolean isBound() 返回套接字的绑定状态。
        boolean isConnected() 返回套接字的连接状态。
        void receive(DatagramPacket p) 从此套接字接收数据报包。
        void send(DatagramPacket p) 从此套接字发送数据报包。
        boolean isClosed() 返回是否关闭了套接字。

      4. java.net.SocketAddress:此类表示不带任何协议附件的 Socket Address
        public abstract class SocketAddress
        提供不可变对象,供套接字用于绑定、连接或用作返回值。
      5. java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
        构造方法
        Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
        Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
        成员方法
        InputStream getInputStream() 返回此套接字的输入流。
        OutputStream getOutputStream() 返回此套接字的输出流。
        void close() 关闭此套接字。
        InetAddress getLocalAddress() 获取套接字绑定的本地地址
        SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点的地址,如果未连接则返回 null。
        int getLocalPort() 返回此套接字绑定到的本地端口
        boolean isBound() 返回套接字的绑定状态
        boolean isConnected() 返回套接字的连接状态
      6. java.net.ServerSocket:此类实现服务器套接字
        构造方法
        ServerSocket(int port) 创建绑定到特定端口的服务器套接字
        成员方法
        Socket accept() 侦听并接受到此套接字的连接
        void bind(SocketAddress endpoint) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)
        void close() 关闭此套接字
        InetAddress getInetAddress() 返回此服务器套接字的本地地址
        int getLocalPort() 返回此套接字在其上侦听的端口
        SocketAddress getLocalSocketAddress() 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null
        boolean isBound() 返回 ServerSocket 的绑定状态
        boolean isClosed() 返回 ServerSocket 的关闭状态
      7. java.io.InputStream:此抽象类是表示字节输入流的所有类的超类
        成员方法
        int available() 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数
        abstract int read() 从输入流中读取数据的下一个字节
        int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
        int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。
      8. java.io.OutputStream:此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器
        成员方法
        abstract void write(int b) 将指定的字节写入此输出流
        void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流
        void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
        void flush() 刷新此输出流并强制写出所有缓冲的输出字节
        void close() 关闭此输出流并释放与此流有关的所有系统资源
    2. 具体编码实现
      1. 流式套接字
        1. 客户端
          1. 建立客户端网络程序,创建socket对象
            可使用IP地址或域名+端口号来构建实例
          2. 交换数据
            使用实例获取基本的输出输入流后,然后使用IO流操作完成数据的传输
          3. 关闭连接,释放资源
          4. 源码
        2. 服务端
          1. 监听并客户端请求,获取请求的socket对象
          2. 交换数据
          3. 关闭连接,释放资源
          4. 源码

              使用实例获取基本的输出输入流后,然后使用IO流操作完成数据的传输
              先使用端口号创建ServerSocket实例,然后调用accept方法
        3. 扩展
          1. 复用socket套接字(不需要每发次信息,建立一个新的连接),不能使用上面方式获取流中字节长度了
            1. 上面相当于电话打通后只对话一次后就关闭连接了
            2. 更加常用的是拨通一次电话后多次对话(复用客户端连接)
            3. 将数据交换的逻辑写到循环中,使用循环依次写或读数组中的数据,只要循环不结束则连接就不会被关闭:如,建立一次连接,发送三次数据
          2. java Socket和ServerSocket多线程编程
            1. 程序分两部分,服务端和客户端。先把服务端运行起来,在运行客户端。整个过程就是客户端想服务端发送String,服务端再返回。客户端的String由键盘输入得到
      2. 包式套接字
        1. 步骤
          1. 创建套接字(即是发收器子是接收器)
          2. 创建要发送或接收的数据包
          3. 发送或接收数据
          4. 关闭资源
          5. 服务端源码
          6. 客户端源码
        2. 说明
          1. 客户端与服务端编码基本相同,只是顺序不同
          2. 客户端创建socket时不需要指定端口号,在数据包中指定
          3. 服务端创建socket时需要指定端口号
          4. UDP方式的同一个网络 连接对象,可以发送到达不同服务器端IP或端口的数据包(群发短信)
    3. 说明
      1. 如果没有监听此端口号会报:Connection refused: connect
      2. 如果端口号被占用会报:Address is already in use
      3. 使用socket获取输入输出流时使用availabel获取流中字节的长度,始终返回0
        1) jdk api文档也说了,available()是“返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数”, 而像socket的话,是网速传输,这个数值是不好估计的,所以没有实现,所以,你最好换个思路去实现你的需求
        源码
        OutputStream或InputStream的available()就是返回0

              对于FileInputStream或FileOutputStream的availabe()是被重写了的
        
          建立一个足够大的字节数组,存储数据,获取数据时,只需要获取0到指定下标的数据即可(虽然方便,但是不建议使用)
              read(字节数组)返回值是:取到的字节的个数
              图解
        
          服务端传递数据时,把数据的字节长度传递过去
              服务端编码
        
              客户端编码
      4. 使用ServerSocket获取的socket对象然后获取的输入流,可以使用availabel获取输入流中的长度
        1)

猜你喜欢

转载自www.cnblogs.com/River111/p/9716601.html