点我

概述

网络编程
  • 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)
基本的通信架构

CS架构:Client客户端、Server服务端

image-20240401195548904

BS架构:Browser浏览器、Server服务端

image-20240401195605382

image-20240401195637650

网络通信三要素

IP地址
  • IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志
  • IP形式有两种:IPv4,IPv6

image-20240402113417275

IPv6地址

  • IPv6:共128位,号称可以为地球每一粒沙子编号
  • IPv6分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开

image-20240402113504649

Java的表示

InetAddress-表示IP地址

名称说明
public static InetAddress getLocalHost()获取本机IP,会以一个inetAddress的对象返回
public static InetAddress getByName(String host)根据ip地址或者域名,返回一个inetAdress对象
public String getHostName()获取该ip地址对象对应的主机名。
public String getHostAddress()获取该ip地址对象中的ip地址信息。
public boolean isReachable(int timeout)在指定毫秒内,判断主机与该ip对应的主机是否能连通

demo

package com.xbxaq.network_;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class Tesk {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress ip1 = InetAddress.getLocalHost();

        // 获取主机名
        System.out.println(ip1.getHostName());

        //获取IP地址
        System.out.println(ip1.getHostAddress());

        //根据指定的IP或者域名的IP地址对象
        InetAddress ip2 = InetAddress.getByName("xbxaq.com");
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress());
    }
}

image-20240402114158533

端口号
  • 标记正在计算机设备上运行的应用程序的,被规定为一个 16 位的二进制,范围是 0~65535
  • 常用端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
  • 注册端口:1024~49151,分配给用户进程或某些应用程序。
  • 动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配

注意:程序员自己开发的程序一般选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则就会出错

通信协议
  • 网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议

OSI网络参考模型-开放式网络互联标准

  • OIS网络参考模型:全球网络互联的标准
  • TCP/IP:事实上的国际标准
OSI网络参考模型TCP/IP网络模型各层对应面向操作
应用层应用层HTTP、FTP、SMTP… ,数据单位还是报文应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发
表示层 数据单位还是报文主要功能是定义数据格式及加密
会话层 称为报文定义了如何开始、控制和结束一个会话,包括对多个双向消息的控制和管理
传输层传输层TCP的数据单元称为段 (segments);UDP协议的数据单元称为“数据报(datagrams是否选择差错恢复协议还是无差错恢复协议
网络层网络层数据的单位称为数据包(packet封装源和目标IP
数据链路层数据链路层+ 物理数据的单位称为帧(frame定义了在单个链路上如何传输数据
物理层 数据单位是比特(bit物理层规范是有关传输介质的特这些规范通常也参考了其他组织制定的标准

UDP通信

特点
  • 无连接、不可靠通信。
  • 不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。
  • 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的

Java提供了一个java.net.DatagramSocket类来实现UDP通信

DatagramSocket:用于创建客户端、服务端

构造器说明
public DatagramSocket()创建客户端的Socket对象, 系统会随机分配一个端口号。
public DatagramSocket(int port)创建服务端的Socket对象, 并指定端口号
方法说明
public void send(DatagramPacketd p)发送数据包
public void receive(DatagramPacket p)使用数据包接收数据

DatagramPacket:创建数据包

构造器说明
public DatagramPacket(byte[] buf, int length, InetAddress address, int port)创建发出去的数据包对象
public DatagramPacket(byte[] buf, int length)创建用来接收数据的数据包
方法说明
public int getLength()获取数据包,实际接收到的字节个数
客户端实现的步骤
  1. 创建DatagramSocket对象(客户端对象)
  2. 创建DatagramPacket对象封装需要发送的数据(数据包对象)
  3. 使用DatagramSocket对象的send方法,传入DatagramPacket对象
  4. 释放资源
服务端实现步骤
  1. 创建DatagramSocket对象并指定端口(服务端对象)
  2. 创建DatagramPacket对象接收数据(数据包对象)
  3. 使用DatagramSocket对象的receive方法,传入DatagramPacket对象
demo

client

package com.xbxaq.network_;


import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

//客户端
public class TeskClient {
    public static void main(String[] args) throws Exception {
        System.out.println("----client----");
        //1 创建客户端对象
        DatagramSocket socket = new DatagramSocket(7777);

        //2 创建数据包对象 封装要发出去的数据
        byte[] bytes = "我是小白学安全的客户端".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 6666);

        //3 发送数据包
        socket.send(packet);

        System.out.println("数据包发送完毕");

        //释放资源
        socket.close();
    }
}

server

package com.xbxaq.network_;

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

public class TeskServer {
    public static void main(String[] args) throws IOException {
        System.out.println("----server----");
        //1 创建一个服务端对象,注册端口
        DatagramSocket socket = new DatagramSocket(6666);

        //2 创建一个数据包对象,用于接收数据
        byte[] bytes = new byte[1024 * 64]; //64kB
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);


        //3 开始接收来自客户端的数据
        socket.receive(packet);

        //4 从字节数组中,将接收到数据打印出来
        int len = packet.getLength();

        String rs = new String(bytes,0,len);
        System.out.println(rs);

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(packet.getPort());

        socket.close();
    }
}

先启动服务端,再启动客户端

image-20240402121425235

image-20240402121431336

UDP的多发多收

客户端实现步骤

  1. 创建DatagramSocket对象(发送端对象)
  2. 使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
  3. 如果用户输入的不是exit, 把数据封装成DatagramPacket
  4. 使用DatagramSocket对象的send方法将数据包对象进行发送
  5. 释放资源

服务端实现步骤

  1. 创建DatagramSocket对象并指定端口(接收端对象)
  2. 创建DatagramPacket对象接收数据(数据包对象)
  3. 使用DatagramSocket对象的receive方法传入DatagramPacket对象
  4. 使用while死循环不断的进行第3步

demo

client端

package com.xbxaq.network_;


import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

//客户端
public class TeskClient {
    public static void main(String[] args) throws Exception {
        System.out.println("----client----");
        //1 创建客户端对象
        DatagramSocket socket = new DatagramSocket(7777);

        Scanner sc = new Scanner(System.in);

        while (true){
            System.out.println("input:");
            String msg = sc.nextLine();

            // 一旦发现用户输入的exit命令,就退出客户端
            if("exit".equals(msg)){
                System.out.println("!退出成功!");
                socket.close(); // 释放资源
                break; // 跳出死循环
            }

            byte[] bytes = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                    , InetAddress.getLocalHost(),  6666);

            // 3、开始正式发送这个数据包的数据出去了
            socket.send(packet);
        }
        //释放资源
        socket.close();
    }
}

server端

package com.xbxaq.network_;

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

public class TeskServer {
    public static void main(String[] args) throws IOException {
        System.out.println("----server----");
        //1 创建一个服务端对象,注册端口
        DatagramSocket socket = new DatagramSocket(6666);

        //2 创建一个数据包对象,用于接收数据
        byte[] bytes = new byte[1024 * 64]; //64kB
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);

        while (true){
            // 3、开始正式使用数据包来接收客户端发来的数据
            socket.receive(packet);

            // 4、从字节数组中,把接收到的数据直接打印出来
            // 接收多少就倒出多少
            // 获取本次数据包接收了多少数据。
            int len = packet.getLength();

            String rs = new String(bytes, 0 , len);
            System.out.println(rs);

            System.out.println(packet.getAddress().getHostAddress());
            System.out.println(packet.getPort());
            System.out.println("--------------------------------------");
        }

    }
}

先启动服务端 再启动客户端

image-20240402122322312

image-20240402122352639

TCP通信

特点
  • 面向连接、可靠通信。
  • TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
  • TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接

Java提供了一个java.net.Socket类来实现TCP通信

客户端
构造器说明
public Socket(String host , int port)根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
方法说明
public OutputStream getOutputStream()获得字节输出流对象
public InputStream getInputStream()获得字节输入流对象

实现步骤

  1. 创建客户端的socket对象,请求与服务器端的连接
  2. 使用socket对象调用getOutputStream()方法得到字节输出流
  3. 使用字节输出流完成数据的发送
  4. 释放资源,关闭socket管道
服务器端
构造器说明
public ServerSocket(int port)为服务端程序注册端口
方法说明
public Socket accept()阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象

服务器端实现步骤

  1. 创建ServerSocket对象, 注册服务端的端口
  2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到socket管道对象
  3. 通过socket对象调用getInputStream()方法得到字节流,完成数据的接收
  4. 释放资源:关闭socket管道
demo

先启动服务端,再启动客户端

client

package com.xbxaq.network_;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;

//客户端
public class TeskClient {
    public static void main(String[] args) throws Exception {
        System.out.println("----client----");
        //1 创建socket对象,并同时请求与服务端程序的连接
        Socket socket = new Socket("127.0.0.1", 8888);

        //2 从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream outputStream = socket.getOutputStream();

        //3 将低级的字节输出流 包装成数据输出流
        DataOutputStream dos = new DataOutputStream(outputStream);
        dos.writeUTF("xbxaq.com");

        //提示
        System.out.println("消息发送完毕");
        socket.close();
    }
}

server

package com.xbxaq.network_;

import java.io.DataInputStream;
import java.io.IOException;;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;


public class TeskServer {
    public static void main(String[] args) throws IOException {
        System.out.println("----server----");

        //1 创建serversocket对象
        ServerSocket serverSocket = new ServerSocket(8888);

        //2 使用serversocket对象,调用accept方法,等待客户端的连接请求
        Socket accept = serverSocket.accept();

        //3 从socket通信管道中得到一个字节流
        InputStream inputStream = accept.getInputStream();

        DataInputStream dataInputStream = new DataInputStream(inputStream);

        //5 使用数据输入流读取客户端发送的消息
        String rs = dataInputStream.readUTF();
        System.out.println(rs);

        //获取客户端的IP地址
        System.out.println(accept.getRemoteSocketAddress());

        dataInputStream.close();
        accept.close();
    }
}

image-20240402175755261

image-20240402175801337

实现多发多收

想法

  • 客户端使用死循环,使得用户不断的输入消息
  • 服务端使用死循环,控制服务端接收到消息之后,继续等待接收下一个消息

demo

client

package com.xbxaq.network_;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

//客户端
public class TeskClient {
    public static void main(String[] args) throws Exception {
        System.out.println("----client----");
        //1 创建socket对象,并同时请求与服务端程序的连接
        Socket socket = new Socket("127.0.0.1", 9999);

        //2 从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream outputStream = socket.getOutputStream();

        //3 将低级的字节输出流 包装成数据输出流
        DataOutputStream dos = new DataOutputStream(outputStream);

        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.println("Input:");
            String msg = scanner.nextLine();

            if ("exit".equals(msg)){
                System.out.println("exit success!!!");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

server

package com.xbxaq.network_;


import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TeskServer {
    public static void main(String[] args) throws Exception {
        System.out.println("----server----");
        //1 创建serversocket对象,同时服务端注册端口
        ServerSocket serverSocket = new ServerSocket(9999);

        //2 使用serversocket对象调用accpet方法
        Socket socket = serverSocket.accept();

        //3 从socket通信管道中得到一个字节流
        InputStream inputStream = socket.getInputStream();

        //4 将原始的字节输入流 包装成数据输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);

        while(true) {
            try {
                String rs = dataInputStream.readUTF();
                System.out.println(rs);
            } catch (Exception e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线中");
                dataInputStream.close();
                socket.close();
                break;
            }
        }
    }
}

image-20240402181233668

服务器如何与多个客户端通信

image-20240402181334682

  • 主线程定义循环负责客户端socket管道连接
  • 每接收到一个socket通信管道后分配一个独立的线程负责处理
分类: JAVA

评论

-- 评论已关闭 --

目录