java网络编程

网络编程

一 网络编程概述

1 网络编程中有三个主要的问题:

  • 问题1:如何准确地定位网络上一台或多台主机
  • 问题2:如何定位主机上的特定的应用
  • 问题3:找到主机后,如何可靠、高效地进行数据传输

2 实现网络传输的三个要素:(对应解决三个问题)

  • 使用IP地址(准确地定位网络上一台或多台主机)
  • 使用端口号(如何定位主机上的特定的应用)
  • 规范网络通信协议(可靠、高效地进行数据传输)

3 通信要素1:IP地址

  • 作用:IP地址用来给网络中的一台计算机设备做唯一的编号

  • IP地址分类:

    方式1:IPv4(占用4个字节)

    ​ IPv6(占用6个字节)

    方式2:公网地址( 万维网使用)和 私有地址( 局域网使用)。192.168.开头的就是私有地址

  • 本地回路地址:127.0.0.1

  • 域名:便捷的记录IP地址

    ​ www.baidu.com
    www.bilibili.com
    www.mi.com

4 通信要素2:端口号

  • 可以唯一标识主机中的进程(应用程序)
  • 不同的进程分配不同的端口号
  • 范围:0-65535

5 通信要素3:通信协议

  • 网络通信协议目的:为了实现可靠而高效的数据传输

  • 网络参考模型:

    OSI参考模型:将网络分为7层,过于理想化,没有实施起来。

    TCP/IP参考模型:将网络分为4层:应用层、传输层、网络层、物理+数据链路层,事实上使用的标准。

6 InetAddress的使用

  1. 作用: InetAddress类的一个实例就代表一个具体的IP地址

  2. 实例化方法:

    InetAddress getByName(String host):获取指定IP对应的InetAddress的实例。

    InetAddress getLocalHost():获取本地IP对应的InetAddress的实例。

    3.常用方法:

​ getHostName()

​ getHostAddress()

4 举例:

package Test3;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressTest {
    public static void main(String[] args) {
        try{
            //1.实例化
            //InetAddress  getByName(String  host):获取指定IP对应的InetAddress的实例。
            InetAddress inet1 = InetAddress.getByName("192.168.23.31");
            System.out.println(inet1);///192.168.23.31
            InetAddress inet2 = InetAddress.getByName("www.mi.com");
            System.out.println(inet2);//www.mi.com/113.240.98.43

            //InetAddress  getLocalHost():获取本地IP对应的InetAddress的实例。
            InetAddress inet3 = InetAddress.getLocalHost();
            System.out.println(inet3);//DESKTOP-2OS0G9O/172.17.110.17
            InetAddress inet4 = InetAddress.getByName("127.0.0.1");
            System.out.println(inet4);///127.0.0.1

            //2.两个常用的方法
            System.out.println(inet2.getHostName());//www.mi.com
            System.out.println(inet2.getHostAddress());//113.240.98.42

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

二 谈传输层协议:TCP与UDP协议

java.net 包中提供了两种常见的网络协议的支持:

  • UDP:用户数据报协议(User Datagram Protocol)。
  • TCP:传输控制协议 (Transmission Control Protocol)。

1 TCP协议与UDP协议

TCP协议:

  • TCP协议进行通信的两个应用进程:客户端、服务端。
  • 使用TCP协议前,须先建立TCP连接,形成基于字节流的传输数据通道
  • 传输前,采用“三次握手”方式,点对点通信,是可靠的
    • TCP协议使用重发机制,当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体确认信息,如果没有收到另一个通信实体确认信息,则会再次重复刚才发送的消息。
  • 在连接中可进行大数据量的传输
  • 传输完毕,需释放已建立的连接,效率低

UDP协议:

  • UDP协议进行通信的两个应用进程:发送端、接收端。
  • 将数据、源、目的封装成数据包(传输的基本单位),不需要建立连接
  • 发送不管对方是否准备好,接收方收到也不确认,不能保证数据的完整性,故是不可靠的
  • 每个数据报的大小限制在64K
  • 发送数据结束时无需释放资源,开销小,通信效率高
  • 适用场景:音频、视频和普通数据的传输。例如视频会议

TCP生活案例:打电话

UDP生活案例:发送短信、发电报

2 三次握手

TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

  • 第一次握手,客户端向服务器端发起TCP连接的请求
  • 第二次握手,服务器端发送针对客户端TCP连接请求的确认
  • 第三次握手,客户端发送确认的确认

完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。

3 四次挥手

TCP协议中,在发送数据结束后,释放连接时需要经过四次挥手。

  • 第一次挥手:客户端向服务器端提出结束连接,让服务器做最后的准备工作。此时,客户端处于半关闭状态,即表示不再向服务器发送数据了,但是还可以接受数据。
  • 第二次挥手:服务器接收到客户端释放连接的请求后,会将最后的数据发给客户端。并告知上层的应用进程不再接收数据。
  • 第三次挥手:服务器发送完数据后,会给客户端发送一个释放连接的报文。那么客户端接收后就知道可以正式释放连接了。
  • 第四次挥手:客户端接收到服务器最后的释放连接报文后,要回复一个彻底断开的报文。这样服务器收到后才会彻底释放连接。这里客户端,发送完最后的报文后,会等待2MSL,因为有可能服务器没有收到最后的报文,那么服务器迟迟没收到,就会再次给客户端发送释放连接的报文,此时客户端在等待时间范围内接收到,会重新发送最后的报文,并重新计时。如果等待2MSL后,没有收到,那么彻底断开。

三 Socket类

  • 网络上具有唯一标识的IP地址和端口号组合在一起构成唯一能识别的标识符套接字(Socket)。

  • 利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。网络通信其实就是Socket间的通信。

  • 通信的两端都要有Socket,是两台机器间通信的端点。

  • Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。

  • 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。

  • Socket分类:

    • 流套接字(stream socket):使用TCP提供可依赖的字节流服务
      • ServerSocket:此类实现TCP服务器套接字。服务器套接字等待请求通过网络传入。
      • Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
    • 数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
      • DatagramSocket:此类表示用来发送和接收UDP数据报包的套接字。

Socket相关类API

ServerSocket类

ServerSocket类的构造方法:

  • ServerSocket(int port) :创建绑定到特定端口的服务器套接字。

ServerSocket类的常用方法:

  • Socket accept():侦听并接受到此套接字的连接。
Socket类

Socket类的常用构造方法

  • public Socket(InetAddress address,int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
  • public Socket(String host,int port):创建一个流套接字并将其连接到指定主机上的指定端口号。

Socket类的常用方法

  • public InputStream getInputStream():返回此套接字的输入流,可以用于接收消息
  • public OutputStream getOutputStream():返回此套接字的输出流,可以用于发送消息
  • public InetAddress getInetAddress():此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 null。
  • public InetAddress getLocalAddress():获取套接字绑定的本地地址。
  • public int getPort():此套接字连接到的远程端口号;如果尚未连接套接字,则返回 0。
  • public int getLocalPort():返回此套接字绑定到的本地端口。如果尚未绑定套接字,则返回 -1。
  • public void close():关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和 OutputStream。
  • public void shutdownInput():如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容,则流将返回 EOF(文件结束符)。 即不能在从此套接字的输入流中接收任何数据。
  • public void shutdownOutput():禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。

注意:先后调用Socket的shutdownInput()和shutdownOutput()方法,仅仅关闭了输入流和输出流,并不等于调用Socket的close()方法。在通信结束后,仍然要调用Scoket的close()方法,因为只有该方法才会释放Socket占用的资源,比如占用的本地端口号等。

四 TCP网络编程

开发步骤

客户端程序包含以下四个基本的步骤 :

  • 创建 Socket :根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
  • 打开连接到 Socket 的输入/ 出流: 使用 getInputStream()方法获得输入流,使用getOutputStream()方法获得输出流,进行数据传输
  • 按照一定的协议对 Socket 进行读/ 写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线路。
  • 关闭 Socket :断开客户端到服务器的连接,释放线路

服务器端程序包含以下四个基本的 步骤:

  • 调用 ServerSocket(int port) :创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求。
  • 调用 accept() :监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象。
  • 调用 该Socket 类对象的 getOutputStream() 和 getInputStream () :获取输出流和输入流,开始网络数据的发送和接收。
  • 关闭Socket 对象:客户端访问结束,关闭通信套接字。

举例:

package Test3;

//客户端发送内容给服务端,服务端将内容打印在控制台上
import org.testng.annotations.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPTest {

@Test
    //客户端
   public void client(){
       Socket socket = null;
       OutputStream os = null;
       try{
           //创建一个Socket
           InetAddress inetAddress = InetAddress.getByName("127.0.0.1");//声明一个IP地址
           int port = 8989;//声明端口号
           socket=  new Socket(inetAddress,port);

           //发送数据
            os = socket.getOutputStream();
           os.write("我是客户端".getBytes());
       }catch(IOException e) {
           e.printStackTrace();
       }finally {
           //关闭
           try {
               if(socket != null){
                   socket.close();
               }
           }catch(IOException e) {
               e.printStackTrace();
           }

           try{
               if(os != null){
                   os.close();
               }
           }catch (IOException e1){
               e1.printStackTrace();
           }
       }
   }

   @Test
   //服务端
   public void server() {
        ServerSocket serverSocket = null;
       Socket socket = null;
       InputStream is = null;
       try {
           int port = 8989;
           ServerSocket serversocket = new ServerSocket(port);

           socket = serversocket.accept();//阻塞式方法
           System.out.println("服务器端已开启");

           is = socket.getInputStream();
           byte[] buffer = new byte[1024];
           int length;
           while ((length = is.read(buffer)) != -1) {
               String str = new String(buffer, 0, length);
               System.out.println(str);
               System.out.println("\n数据接收完毕");
           }
       } catch (IOException e) {
           e.printStackTrace();
       } finally {

           try {
               if (socket != null) {
                   socket.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }

           try {
               if (is != null) {
                   is.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }

           try {
               if (serverSocket != null) {
                   serverSocket.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }

       }
   }

}

五 UDP网络编程

UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境,数据报大小限制在64K以下。

1 开发步骤

发送端程序包含以下四个基本的步骤:

  • 创建DatagramSocket :默认使用系统随机分配端口号。
  • 创建DatagramPacket:将要发送的数据用字节数组表示,并指定要发送的数据长度,接收方的IP地址和端口号。
  • 调用 该DatagramSocket 类对象的 send方法 :发送数据报DatagramPacket对象。
  • 关闭DatagramSocket 对象:发送端程序结束,关闭通信套接字。

接收端程序包含以下四个基本的步骤 :

  • 创建DatagramSocket :指定监听的端口号。
  • 创建DatagramPacket:指定接收数据用的字节数组,起到临时数据缓冲区的效果,并指定最大可以接收的数据长度。
  • 调用 该DatagramSocket 类对象的receive方法 :接收数据报DatagramPacket对象。。
  • 关闭DatagramSocket :接收端程序结束,关闭通信套接字。

2 DatagramSocket类

DatagramSocket 类的常用方法:

  • public DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被绑定到通配符地址,IP 地址由内核来选择。
  • public DatagramSocket(int port,InetAddress laddr)创建数据报套接字,将其绑定到指定的本地地址。本地端口必须在 0 到 65535 之间(包括两者)。如果 IP 地址为 0.0.0.0,套接字将被绑定到通配符地址,IP 地址由内核选择。
  • public void close()关闭此数据报套接字。
  • public void send(DatagramPacket p)从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。
  • public void receive(DatagramPacket p)从此套接字接收数据报包。当此方法返回时,DatagramPacket 的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法在接收到数据报前一直阻塞。数据报包对象的 length 字段包含所接收信息的长度。如果信息比包的长度长,该信息将被截短。
  • public InetAddress getLocalAddress()获取套接字绑定的本地地址。
  • public int getLocalPort()返回此套接字绑定的本地主机上的端口号。
  • public InetAddress getInetAddress()返回此套接字连接的地址。如果套接字未连接,则返回 null。
  • public int getPort()返回此套接字的端口。如果套接字未连接,则返回 -1。

3 DatagramPacket类

DatagramPacket类的常用方法:

  • public DatagramPacket(byte[] buf,int length)构造 DatagramPacket,用来接收长度为 length 的数据包。 length 参数必须小于等于 buf.length。
  • public DatagramPacket(byte[] buf,int length,InetAddress address,int port)构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length 参数必须小于等于 buf.length。
  • public InetAddress getAddress()返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
  • public int getPort()返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
  • public byte[] getData()返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量 offset 处开始,持续 length 长度。
  • public int getLength()返回将要发送或接收到的数据的长度。

4 举例:

package Test3;

import org.testng.annotations.Test;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


public class UDPTest {

    //发送端
    @Test
    public void sender() throws Exception {
        DatagramSocket ds = new DatagramSocket();

        InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
        int port = 9090;
        byte[] bytes = "我是发送端".getBytes("utf-8");

        DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,inetAddress,port);

        ds.send(packet);

        ds.close();

    }


    //接收端
    @Test
    public void receiver() throws IOException {
        int port = 9090;
        DatagramSocket ds = new DatagramSocket(port);

        byte[] buffer = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);

        ds.receive(packet);

        String str = new String(packet.getData(),0,packet.getLength());
        System.out.println(str);

        ds.close();

    }
}

六 URL编程

1 URL类

  • URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上某一资源的地址。

  • 通过 URL 我们可以访问 Internet 上的各种网络资源,比如最常见的 www,ftp 站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。

  • URL的基本结构由5部分组成:

<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表

例如:

http://192.168.21.107:8080/examples/abcd.jpg?name=Tom

2 URL类常用方法

一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的方法来获取这些属性:

  • public String getProtocol( ) 获取该URL的协议名

  • public String getHost( ) 获取该URL的主机名

  • public String getPort( ) 获取该URL的端口号

  • public String getPath( ) 获取该URL的文件路径

  • public String getFile( ) 获取该URL的文件名

  • public String getQuery( ) 获取该URL的查询名

举例:

package Test3;


import java.net.MalformedURLException;
import java.net.URL;

public class URLTest {
    public static void main(String[] args) {
        String str = "http://192.168.21.107:8080/examples/abcd.jpg?name=Tom";
        URL url;

        {
            try {
                url = new URL(str);

                System.out.println(url.getProtocol());//获取该URL的协议名
                System.out.println(url.getHost());//获取该URL的主机名
                System.out.println(url.getPort());//获取该URL的端口名
                System.out.println(url.getPath());//获取该URL的文件路径
                System.out.println(url.getFile());//获取该URL的文件名
                System.out.println(url.getQuery());//获取该URL的查询名
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
    }
}
//运行结果:
//http
//192.168.21.107
//8080
///examples/abcd.jpg
///examples/abcd.jpg?name=Tom
//name=Tom

3 针对HTTP协议的URLConnection类

  • URL的方法 openStream():能从网络上读取数据
  • 若希望输出数据,例如向服务器端的 CGI (公共网关接口-Common Gateway Interface-的简称,是用户浏览器和服务器端的应用程序进行连接的接口)程序发送一些数据,则必须先与URL建立连接,然后才能对其进行读写,此时需要使用 URLConnection 。
  • URLConnection:表示到URL所引用的远程对象的连接。当与一个URL建立连接时,首先要在一个 URL 对象上通过方法 openConnection() 生成对应的 URLConnection 对象。如果连接过程失败,将产生IOException.
    • URL netchinaren = new URL (“http://192.168.21.107:8080/examples/abcd.jpg?name=Tom”);
    • URLConnectonn u = netchinaren.openConnection( );

举例:

package com.test2;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class Download {
    public static void main(String[] args) throws Exception {
        URL url = new URL("https://dl.360nate.com/setup.exe");
        URLConnection con = url.openConnection();
        con.connect();
        try (FileOutputStream fos = new FileOutputStream("D:\\360.exe"); InputStream is = con.getInputStream()) {
            int len = -1;
            byte[] bt = new byte[20480];
            while ((len = is.read(bt)) != -1) {
                fos.write(bt, 0, len);
            }
            fos.flush();
            System.out.println("下载完成...");
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值