网路编程的主要问题

  • 如何准确的定位到网络上的一台或者多台主机。
  • 找到主机后如何进行通信。

要素

  • IP 和 端口号

  • 网络通信协议:UDP TCP

ip和端口

/*
InetAddress没有构造方法,所以直接调用他的静态方法使用
 */
public static void main(String[] args) {
    try {
        //查询本机IP地址的方法
        InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
        System.out.println(inetAddress1);
        InetAddress inetAddress2 = InetAddress.getByName("localhost");
        System.out.println(inetAddress2);
        InetAddress inetAddress3 = InetAddress.getLocalHost();
        System.out.println(inetAddress3);

        //查询网络ip
        InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com");
        System.out.println(inetAddress4);

        //常用方法
        System.out.println("-------方法-------");
        System.out.println(inetAddress4.getAddress());//获取的是一个字节数组
        System.out.println(inetAddress4.getCanonicalHostName());//获取规范的名字
        System.out.println(inetAddress4.getHostAddress());//ip
        System.out.println(inetAddress4.getHostName());//域名,如果是自己电脑就是计算机的名字
    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
}
  • 端口

    端口就是和一个程序对应的,每个程序都有自己的端口号,通过这个端口号可以和这个程序进行通信。

    规定为0- 65536

  • 传输层的协议:TCP、UDP,单个端口下端口号不能冲突。

public static void main(String[] args) {
    InetSocketAddress socketAddress1 = new InetSocketAddress("127.0.0.1", 8080);
    InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 8080);
    System.out.println(socketAddress1);
    System.out.println(socketAddress2);
    
    System.out.println(socketAddress1.getPort());//获取端口号
}

通信协议

TCP

  • TCP协议全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的RFC 793定义。TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,你可以把它想象成排水管中的水流。(就是说需要建立连接).

  • 三次握手,四次挥手:

    第一次握手

    客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。

    第二次握手

    服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。

    第三次握手

    当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。

    四次挥手

    1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
    2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间.
    3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
    4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
    5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
    6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
      ————————————————

    原文链接

UDP:无连接的协议。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。

TCP建立通信

关键serverSocket类用于服务端创建端口,而客户端通过InetAddress的静态方法设置需要访问的主机也就是服务器的ip,然后客户端通过Socket类创建连接。

  • 步骤:

    • 建立服务端和客户端
    • 服务端:1.建立端口serverSocket。2.等待用户的连接(监听用户的连接)accept。3.接收客户端的消息。
    • 客户端:1.连接到服务器端 Socket。2.发送消息。
  • 代码测试:

    1.发送消息给服务端并打印

public class DemoClient01 {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = null;
        Socket socket = null;
        try {
            //1.先找到服务端的地址也就是ip(这里是本机所以是127.0.0.1)
             InetAddress addressIP = InetAddress.getByName("127.0.0.1");
            //2.创建一个Socket连接端口
            int port = 9999;
             socket = new Socket(addressIP, port);
            //3.发送消息
            outputStream = socket.getOutputStream();
            outputStream.write("哈哈哈哈哈哈哈哈哈哈!!!".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            outputStream.close();
            socket.close();
        }
    }
}

/**
 * @author:Mr.Han
 * @description:客户端
 * @date:2020/4/12
 */
public class DemoServer01 {
    public static void main(String[] args) throws IOException {
            //1.创建一个端口号
            ServerSocket serverSocket = new ServerSocket(9999);
            //2.监听客户端的socket(这里的sorket就是客户端的)
            Socket socket = serverSocket.accept();
            //3.接收消息
            InputStream msg = socket.getInputStream();
            //4.打印消息
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();//管道流
            byte[] buffer = new byte[1024];
            int len = 0;
            while((len=(msg.read(buffer)))!=-1){
                //将接收到的消息全写进ByteArrayOutputStream
                outputStream.write(buffer);
            }
            System.out.println(outputStream.toString());
        outputStream.close();
        msg.close();
        socket.close();
    }
}

2。测试发送文件

/**
 * @author:Mr.Han
 * @description:服务端接收文件并输出到文件夹
 * @date:2020/4/12
 */
public class DemoServer02 {
    public static void main(String[] args) throws Exception {
        //建立端口
        ServerSocket serverSocket = new ServerSocket(9999);
        //监听
        Socket socket = serverSocket.accept();
        //获取内容
        InputStream is = socket.getInputStream();
        //将获取的文件写出到文件夹
        FileOutputStream fos = new FileOutputStream("recive.jpg");
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len=is.read(buffer))!=-1){
            fos.write(buffer);
        }
        //告诉client接收完毕
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("接收完毕哈哈哈!".getBytes());
        
        outputStream.close();
        fos.close();
        is.close();
        socket.close();
    }
}
/**
 * @author:Mr.Han
 * @description:客户端发送文件
 * @date:2020/4/12
 */
public class DemoClient02 {
    public static void main(String[] args) throws Exception {
        //创建与服务器的连接socket
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
        //获取文件
        FileInputStream fis = new FileInputStream(new File("C:\\Users\\M.han\\IdeaProjects\\JUCstudy\\src\\test\\java\\net\\book.jpg"));
        //发送文件
        OutputStream outputStream = socket.getOutputStream();
        int len;
        byte[] buffer = new byte[1024];
        while((len=fis.read(buffer))!=-1){
           outputStream.write(buffer);
        }
        //通知服务器结束了传输
        socket.shutdownOutput();
        //接收服务器接收完返回来的消息
        InputStream inmsg = socket.getInputStream();
        byte[] buffer2 = new byte[1024];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        while ((len=inmsg.read(buffer2))!=-1){
            byteArrayOutputStream.write(buffer2);
        }
        System.out.println("服务器消息:"+byteArrayOutputStream.toString());
        byteArrayOutputStream.close();
        inmsg.close();
        outputStream.close();
        fis.close();
    }
}

UDP通信

关键DatagramSocket类用于创建socket,DatagramPacket类用于创建需要发送的包或者接收的包。

UDP不需要创建端口,但是需要注意发送方和接收方。

/**
 * @author:Mr.Han
 * @description:udp方式通信
 * @date:2020/4/12
 */
public class UDPDemo01 {
    public static void main(String[] args) throws Exception {
        //1.建立socket
        DatagramSocket datagramSocket = new DatagramSocket();
        //2.建立包
        String str = "哈哈哈哈哈服务器!!";
        InetAddress localhost = InetAddress.getByName("localhost");
        int port = 9999;
        DatagramPacket packet = new DatagramPacket(str.getBytes(), 0, str.getBytes().length, localhost, port);
        //3.发送包
        datagramSocket.send(packet);

        datagramSocket.close();
    }
}
/**
 * @author:Mr.Han
 * @description:udp方式通信
 * @date:2020/4/12
 */
public class UDPDemo02 {
    public static void main(String[] args) throws Exception {
        //建立Socket
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        //建立接收包
        byte[] buffer = new byte[1024];//最多只能接收这么多字节的内容
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        //接收
        datagramSocket.receive(packet);

        //获取包的内容
        System.out.println(packet.getAddress());
        System.out.println(new String(packet.getData()));

        datagramSocket.close();
    }
}

发送消息

/**
 * @author:Mr.Han
 * @description:用户1
 * @date:2020/4/12
 */
public class Chat01 {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9999);

        Scanner input = new Scanner(System.in);
        while(true){
            //创建发送包
            String data = input.nextLine();
            byte[] datas = data.getBytes();
            DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, InetAddress.getByName("localhost"), 9090);
           
            socket.send(packet);
            if ("bye".equals(data)){
                break;
            }
        }
        input.close();
        socket.close();
    }
}
/**
 * @author:Mr.Han
 * @description:用户2,只能接收
 * @date:2020/4/12
 */
public class Chat02 {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9090);
        while (true){
            //创建接收包
            byte[] datas = new byte[1024];
            DatagramPacket packet = new DatagramPacket(datas,0,datas.length);

            socket.receive(packet);
            //将内容转字符串
            String str = new String(packet.getData(),0,packet.getData().length).trim();//trim去掉字符串后边的空格

            System.out.println(packet.getAddress() + ":" + str);
            //终止聊天
            if ("bye".equals(str)){
                break;
            }
        }
        socket.close();
    }
}

线程实现相互聊天

/*
	思路:创建两个线程类,用于发消息和接收消息。
*/
public class Send implements Runnable {
    DatagramSocket socket = null;
    Scanner input = null;
    InetAddress host = null;
    int toPort;
    int fromPort;

    public Send(int toPort,InetAddress host ,int fromPort) {
        this.toPort = toPort;
        this.host = host;
        this.fromPort = fromPort;
        try {
            socket  = new DatagramSocket(fromPort);
            input = new Scanner(System.in);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        while(true){
            try {
                //创建发送包
                String data = input.nextLine();
                byte[] datas = data.getBytes();
                DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, host, toPort);
                socket.send(packet);
                if ("bye".equals(data)){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        socket.close();
    }
}

public class Recieve implements Runnable{
    DatagramSocket socket = null;
    int rePort;
    String name;

    public Recieve(int report,String name) {
        this.rePort = report;
        this.name = name;
        try {
            socket = new DatagramSocket(report);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true){
            try {
                //创建接收包
                byte[] datas = new byte[1024];
                DatagramPacket packet = new DatagramPacket(datas,0,datas.length);

                socket.receive(packet);
                //将内容转字符串
                String str = new String(packet.getData(),0,packet.getData().length).trim();//trim去掉字符串后边的空格

                System.out.println(this.name+ ":" + str);
                //终止聊天
                if ("bye".equals(str)){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        socket.close();
    }
}

/*
	注意UDP发送的特点,不需要和对方建立联系,直接发送对方就能收到。
*/
//老师类
 public static void main(String[] args) throws UnknownHostException {
     //老师发送到学生接收端口9090
        new Thread(new Send(9090, InetAddress.getByName("localhost"),3333)).start();
        new Thread(new Recieve(9999,"学生")).start();//老师负责接收的端口是9999,收的是学生的消息
    }
//学生类
 public static void main(String[] args) throws UnknownHostException {
     //学生发送到老师的接收端口9999
        new Thread(new Send(9999, InetAddress.getByName("localhost"),2222)).start();
        new Thread(new Recieve(9090,"老师")).start();//学生负责接收的端口是9090,收的老师的消息

    }

下载网络资源URL

public class URLDemo {
    public static void main(String[] args) {
        InputStream is = null;
        HttpURLConnection connection = null;
        FileOutputStream fos = null;
        try {
            URL url = new URL("https://........");
            connection = (HttpURLConnection) url.openConnection();
            is = connection.getInputStream();
            fos = new FileOutputStream("1.jpg");

            int len;
            byte[] buffer = new byte[1024];
            while ((len=is.read(buffer))!=-1){
                fos.write(buffer,0,len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            connection.disconnect();
        }
    }
}
 URL url = new URL("https://........");
        System.out.println("端口-"+url.getPort());
        System.out.println("主机ip-"+url.getHost());
        System.out.println("文件-"+url.getPath());
        System.out.println("全路径-"+url.getFile());
        System.out.println("参数-"+url.getQuery());
        System.out.println("协议-"+url.getProtocol());

版权声明:本文为Mr-hanexp原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Mr-hanexp/p/12686484.html