网络编程,网络通信三要素(IP地址,端口号,协议) ,UDP通信 ,TCP通信 ,多线程改进


网络编程

什么是网络编程
可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。

基本的通信架构
基本的通信架构有2种形式:CS架构( Client客户端/Server服务端 ) 、 BS架构(Browser浏览器/Server服务端)。

1.网络通信三要素(IP地址,端口号,协议)

①IP地址

IP地址:表示设备在网络中的地址,是网络中设备的唯一标识
IP地址有两种形式:IPv4、IPv6
IPV4,地址由32个比特位(4个字节)组成,采用二进制太不容易阅读了,于是就将每8位看成一组,把每一组用十进制表示(叫做点分十进制表示法)。所以就有了我们经常看到的IP地址形式,如:192.168.1.66
IPv6,共128位,分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开。
公网IP:是可以连接互联网的IP地址;
内网IP:也叫局域网IP,只能组织机构内部使用。
192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用。
特殊IP地址:
127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机。

IP常用命令:
ipconfig:查看本机IP地址。
ping IP地址:检查网络是否连通。

InetAddress:代表IP地址。
InetAddress的常用方法如下

名称说明
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对应的主机是否能连通
public class InetAddressTest {
    public static void main(String[] args) throws Exception {
        // 1、获取本机IP地址对象的
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());

        // 2、获取指定IP或者域名的IP地址对象。
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress());

        // ping www.baidu.com
        System.out.println(ip2.isReachable(6000));
    }
}

②端口号

端口:标记正在计算机设备上运行的应用程序的,被规定为一个 16 位的二进制,范围是 0~65535。
端口号:应用程序在设备中唯一的标识
分类
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序。
动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。

③协议
协议:连接和数据在网络中传输的规则。

通信协议:网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
开放式网络互联标准:OSI网络参考模型
OSI网络参考模型:全球网络互联标准。
TCP/IP网络模型:事实上的国际标准。

在这里插入图片描述

传输层的2个通信协议
UDP(User Datagram Protocol):用户数据报协议;
TCP(Transmission Control Protocol) :传输控制协议。

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

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

2.UDP通信

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

DatagramSocke用于创建客户端、服务端

构造器说明
public DatagramSocket()创建客户端的Socket对象, 系统会随机分配一个端口号。
public DatagramSocket(int port)创建服务端的Socket对象, 并指定端口号
方法说明
public void send(DatagramPacket dp)发送数据包
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()获取数据包,实际接收到的字节个数

UDP通信:实现1发1收。
客户端实现步骤

①创建DatagramSocket对象(客户端对象)
②创建DatagramPacket对象封装需要发送的数据(数据包对象)
③使用DatagramSocket对象的send方法,传入DatagramPacket对象
④释放资源

public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket(7777);

        // 2、创建数据包对象封装要发出去的数据
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */
        byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                , InetAddress.getLocalHost(),  6666);

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

        System.out.println("客户端数据发送完毕~~~");
        socket.close(); // 释放资源!
    }
}

服务端实现步骤

①创建DatagramSocket对象并指定端口(服务端对象)
②创建DatagramPacket对象接收数据(数据包对象)
③使用DatagramSocket对象的receive方法,传入DatagramPacket对象
④释放资源

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

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

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

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

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

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

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

客户端可以反复发送数据

客户端实现步骤

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

public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            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);
        }
    }
}

接收端实现步骤

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

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

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

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

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

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

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

3.TCP通信

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

客户端程序就是通过java.net包下的Socket类来实现的。

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

客户端实现步骤

①创建客户端的Socket对象,请求与服务端的连接。
②使用socket对象调用getOutputStream()方法得到字节输出流。
③使用字节输出流完成数据的发送。
④释放资源:关闭socket管道。

public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

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

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

        // 4、开始写数据出去了
        dos.writeUTF("在一起,好吗?");
        dos.close();

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

服务端是通过java.net包下的ServerSocket类来实现的。

ServerSocket

构造器说明
public ServerSocket(int port)为服务端程序注册端口
方法说明
public Socket accept()阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象。

服务端实现步骤

①创建ServerSocket对象,注册服务端端口。
②调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
③通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
④释放资源:关闭socket管道

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

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

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        // 5、使用数据输入流读取客户端发送过来的消息
        String rs = dis.readUTF();
        System.out.println(rs);
        // 其实我们也可以获取客户端的IP地址
        System.out.println(socket.getRemoteSocketAddress());

        dis.close();
        socket.close();
    }
}

使用TCP通信实现:多发多收消息。

①客户端使用死循环,让用户不断输入消息。
②服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息。

TCP客户端

/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

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

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

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }

            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

TCP服务端

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

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

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        while (true) {
            try {
                // 5、使用数据输入流读取客户端发送过来的消息
                String rs = dis.readUTF();
                System.out.println(rs);
            } catch (Exception e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线了!");
                dis.close();
                socket.close();
                break;
            }
        }
    }
}

4.多线程改进

每当有一个客户端连接服务端,在服务端这边就为Socket开启一条线程取执行读取数据的操作,来多少个客户端,就有多少条线程。按照这样的设计,服务端就可以支持多个客户端连接了。

服务端的读取数据的线程类

public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);

                } catch (Exception e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端的主程序代码

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }
    }
}
  • 30
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值