JAVA网络编程

本文深入讲解Java网络编程,包括网络基础知识、TCP/IP协议、Socket编程、InetAddress与URL类使用,以及TCP和UDP编程示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通成为了现实。

而在网络编程中使用最多的就是Socket。Java提供了一组功能强大的网络开发类。

一.网络基础知识:

1.两台计算机通过网络进行通信,需要满足一些必备的条件:

(1)IP地址:为实现网络中不同计算机之间的通信,每台机器都必须有一个唯一的标识。用来表示所处的身份,所在的位置。

         IP地址格式:数字型,如:192.168.0.1       

(2)协议:共同的语言用来交流。

(3)端口号:一台主机上可以同时运行多个应用程序,用于区分不同应用程序的通信。

         端口号范围为:0~65535,其中0~1023为系统所保留。

         常用端口号:http  80       ftp  21     telnet   23

 2.TCP/IP协议

  • TCP/IP是目前世界上应用最为广泛的协议。是以TCP和IP为基础的不同层次上多个协议的集合。也称TCP/IP协议族或TCP/IP协议栈;
  • TCP:Transmission Control Protocol 传输控制协议
  • IP:Internet Protocol 互联网协议

3.TCP/IP模型

   5层模型:

      应用层:如HTTP超文件传输协议、FTP文件传输协议、SMTP简单邮件传输协议、Telnet远程登录服务

      传输层:如TCP/IP协议

      网络层

      数据链路层

      物理层:用户最直观接触到的,如网线、网卡等

4.Socket

   IP地址和端口号组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP的基础。

5.JAVA中的网络支持

针对网络通信的不同层次,JAVA提供的网络功能有四大类:

(1)InetAddress:用于标识网络上的硬件资源(标识IP地址相关信息)。

(2)URL:统一资源定位符 ,通过URL可以直接读取或写入网络上的数据(用来表示网络上的资源)。

(3)Sockets:使用TCP协议实现网络通信的Socket相关的类。

(4)Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通信。

二.Java中网络相关的API的应用

1.InetAddress类的应用

InetAddress类用于标识网络上的硬件资源,表示互联网协议 (IP)地址。

InetAddress类没有构造方法,所以不能直接new对象,但可以根据其提供的静态方法获取实例。

package function.socket;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

/**
 * InetAddress类的使用
 *
 * @author kimtian
 */
public class InetAddressOperate {
    public static void main(String[] args) throws UnknownHostException {
        //获取本机的InetAddress实例
        InetAddress localAddress = InetAddress.getLocalHost();
        System.out.println("获取当前计算机的名称:" + localAddress.getHostName());
        System.out.println("获取主机的IP地址:" + localAddress.getHostAddress());
        //获取字节数组形式的IP地址
        byte[] bytes = localAddress.getAddress();
        System.out.println("字节数组形式的IP:" + Arrays.toString(bytes));
        //直接输出InetAddress对象--会输出用户名和IP地址
        System.out.println(localAddress);

        //根据主机名获取InetAddress实例
        InetAddress inetAddress = InetAddress.getByName("KIMTIAN-MC0");
        System.out.println(inetAddress);

        //根据IP地址获取相应的实例信息
        InetAddress inetAddress2 = InetAddress.getByName("10.43.19.128");
        System.out.println(inetAddress2);
    }
}

2.URL的应用

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

  •    URL由两部分组成:协议名称和资源名称,中间用冒号隔开。
  •    在java.net包中,提供了URL类来表示URL。
  •    URL类表示统一资源定位符,指向互联网上的“资源”的指针。
package function.socket;

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

/**
 * URL类的基本操作
 *
 * @author kimtian
 */
public class UrlOperateOne {
    public static void main(String[] args) {
        try {
            //创建一个URL实例
            URL gdt = new URL("https://e.qq.com");
            //?号后面表示参数   #号后面表示锚点
            URL url = new URL(gdt, "/resources?username=kimtian#test");
            //获取相关信息====================
            System.out.println("协议信息:" + url.getProtocol());
            System.out.println("主机信息:" + url.getHost());
            //没有指定端口号,则则根据协议的不同使用默认的端口号,此时getPort()方法返回值为-1
            System.out.println("端口信息:" + url.getPort());
            System.out.println("文件路径:" + url.getPath());
            //文件路径
            System.out.println("文件名称:" + url.getFile());
            //锚点
            System.out.println("相对路径:" + url.getRef());
            //参数
            System.out.println("查询字符串:" + url.getQuery());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

    }
}

(2)使用URL读取网页内容

  •   通过URL对象的openStream()方法可以得到指定资源的输入流。
  •   通过输入流可以读取、访问网络上的数据。  
package function.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 使用URL读取网页内容
 *
 * @author kimtian
 */
public class UrlOperateTwo {
    public static void main(String[] args) {
        try {
            //创建一个URL实例
            URL url = new URL("https://e.qq.com/ads/");
            //通过URL的openStream方法获取URL对象所表示的资源的字节输入流
            InputStream inputStream = url.openStream();
            //将字节输入流转化为字符输入流,可以指定编码
            InputStreamReader isr = new InputStreamReader(inputStream, "utf-8");
            //为字符输入流添加缓冲
            BufferedReader br = new BufferedReader(isr);
            //读取数据
            String data = br.readLine();
            while (data != null) {
                //输出数据
                System.out.println(data);
                //读取下一行数据
                data = br.readLine();
            }
            //关闭资源
            br.close();
            isr.close();
            inputStream.close();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

三.Socket

1.通过Socket实现TCP编程

(1)TCP协议:TCP协议是面向连接、可靠的、有序的,以字节流的方式发送数据。在数据通讯前需要建立连接,保证数据通讯的有效性。

(2)基于TCP协议实现网络通信的类:

  •   客户端Socket类
  •   服务器端的ServerSocket类

(3)Socket通信实现原理:

        1)服务端倾听Socket:建立一个ServerSocket(服务器Socket),绑定相应的端口,并且在指定端口进行倾听,等待客户端的连接。

        2)客户端创建Socket,并向服务器端发送请求。

        3)服务器收到请求,并接受客户端请求信息。

        4)接收请求后,建立连接,创建连接Socket,用来与客户端Socket进行通信。

        5)通过相关的InpuStream和OutputStream进行数据的交换、数据的发送、接受以及数据的响应等。

        6)分别关闭两端的Socket以及相关资源,进行通信的断开。

(4)Socket通信实现步骤:

        1)创建ServerSocket和Socket;

        2)打开连接到Socket的输入/输出流;

        3)按照协议对Socket进行读/写操作;

        4)关闭输入、输出流,关闭Socket。

(5)ServerSocket类:此类实现服务器套接字,服务器套接字等待请求通过网络传入。

package function.socket.tcp;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 基于TCP协议的Socket通信,实现用户登录
 * 服务器端
 *
 * @author kimtian
 */
public class Server {
    /**
     * 服务器端实现步骤:
     * 1.创建ServerSocket对象,绑定监听端口
     * 2.通过accept()方法监听客户端请求
     * 3.建立连接后,通过输入流读取客户端发送的请求信息
     * 4.通过输出流向客户端发送响应信息
     * 5.关闭相关资源
     */
    public static void main(String[] args) {
        try {
            //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
            ServerSocket serverSocket = new ServerSocket(8888);
            //2.调用accept()方法开始监听,等待客户端的连接
            System.out.println("********服务器即将启动,等待客户端的连接**********");
            //一旦调用后处于阻塞情况,等待客户端监听
            Socket socket = serverSocket.accept();
            //3.获取输入流,并读取客户端信息
            //字节输入流
            InputStream is = socket.getInputStream();
            //将字节流转化为字符流
            InputStreamReader isr = new InputStreamReader(is);
            //为输入流添加缓冲
            BufferedReader br = new BufferedReader(isr);
            String info = null;
            //循环读取客户端信息
            while ((info = br.readLine()) != null) {
                System.out.println("我是服务器,客户端说:" + info);
            }

            //关闭输入流
            socket.shutdownInput();
            //4.获取输出流,响应客户端的请求
            OutputStream os = socket.getOutputStream();
            //将输出流包装成打印流
            PrintWriter pw = new PrintWriter(os);
            //要发送的信息
            pw.write("欢迎您!我已经接受到了你的请求了。");
            //调用flush()方法将缓存输出
            pw.flush();
            //关闭当前Socket的输出流
            socket.shutdownOutput();
            //5.关闭相关资源
            pw.close();
            os.close();
            br.close();
            is.close();
            isr.close();
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(6)Socket类:此类实现客户端的套接字。套接字是两台机器间通信的端点。

package function.socket.tcp;

import java.io.*;
import java.net.Socket;

/**
 * 客户端
 *
 * @author kimtian
 */
public class Client {
    /**
     * 客户端:
     * 1.创建Socket对象,指明需要连接的服务器的地址和端口号
     * 2.建立连接后,通过输出流向服务器端发送请求信息
     * 3.通过输入流获取服务器响应的信息
     * 4.关闭相关资源
     */
    public static void main(String[] args) {

        try {
            //1.创建客户端Socket,指定服务器地址和端口
            Socket socket = new Socket("127.0.0.1", 8888);
            //2.获取输出流,向服务器端发送信息
            //获取的是字节输出流
            OutputStream os = socket.getOutputStream();
            //将输出流包装成打印流
            PrintWriter pw = new PrintWriter(os);
            //要发送的信息
            pw.write("username:kimtian;password:123");
            //去刷新缓存,向服务器端发送信息
            pw.flush();
            //关闭当前Socket的输出流
            socket.shutdownOutput();
            //3.获取输入流,并读取服务端响应信息
            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String info = null;
            //循环读取服务器端信息
            while ((info = br.readLine()) != null) {
                System.out.println("我是客户端,服务器端说:" + info);
            }
            //4.关闭相关资源
            br.close();
            is.close();
            isr.close();
            //对于同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可                                
            //pw.close();                                                                                     
            os.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端和服务器端通信运行结果如下图:

在运行时一定要先运行服务器端,再运行客户端。

服务器端结果:

客户端结果:

 (7)使用多线程实现多客户端之间的通信

  多线程服务器实现类:

package function.socket.tcp;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 多线程服务器
 *
 * @author kimtian
 */
public class MultiThreadedServer {
    /**
     * 1.服务器端创建ServerSocket,循环调用accept()等待客户端连接
     * 2.客户端创建一个socket并请求和服务器端连接
     * 3.服务器端接受客户端请求,创建socket与该客户建立专线连接
     * 4.建立连接的两个socket在一个单独的线程上对话
     * 5.服务器端继续等待新的连接
     */
    public static void main(String[] args) {
        try {
            //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
            ServerSocket serverSocket = new ServerSocket(8888);
            //2.调用accept()方法开始监听,等待客户端的连接
            System.out.println("********服务器即将启动,等待客户端的连接**********");
            Socket socket = null;
            //定义一个变量记录客户端的数量
            int count = 0;
            //循环监听等待客户端监听
            while (true) {
                socket = serverSocket.accept();
                //创建一个新的线程
                ServerThread serverThread = new ServerThread(socket);
                //设置线程的优先级,范围为[1,10],默认为5
                //未设置优先级可能会导致运行时速度非常慢,可降低优先级
                serverThread.setPriority(4);
                //启动线程
                serverThread.start();
                //统计客户端的数量
                count++;
                System.out.println("客户端的数量为:" + count);
                InetAddress inetAddress = socket.getInetAddress();
                System.out.println("当前客户端的ip为:"+inetAddress);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  服务器端线程处理类:

package function.socket.tcp;


import java.io.*;
import java.net.Socket;

/**
 * 服务器端线程处理类
 *
 * @author kimtian
 */
public class ServerThread extends Thread {
    /**
     * 和本线程相关的Socket
     */
    Socket socket = null;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    /**
     * 线程执行的操作,响应客户端的请求
     */
    @Override
    public void run() {
        InputStream is = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        OutputStream os = null;
        PrintWriter pw = null;
        try {
            //获取输入流,并读取客户端信息
            //字节输入流
            is = socket.getInputStream();
            //将字节流转化为字符流
            isr = new InputStreamReader(is);
            //为输入流添加缓冲
            br = new BufferedReader(isr);
            String info = null;
            //循环读取客户端信息
            while ((info = br.readLine()) != null) {
                System.out.println("我是服务器,客户端说:" + info);
            }

            //关闭输入流
            socket.shutdownInput();
            //获取输出流,响应客户端的请求
            os = socket.getOutputStream();
            //将输出流包装成打印流
            pw = new PrintWriter(os);
            //要发送的信息
            pw.write("欢迎您!我已经接受到了你的请求了。");
            //调用flush()方法将缓存输出
            pw.flush();
            //关闭当前Socket的输出流
            socket.shutdownOutput();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭相关资源
                if (pw != null) {
                    pw.close();
                }
                if (os != null) {
                    os.close();
                }
                if (br != null) {
                    br.close();
                }
                if (is != null) {
                    is.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

  运行结果如下:

2.通过Socket实现UDP编程

(1)UDP协议:用户数据报协议是无连接、不可靠、无序的。

(2)UDP协议以数据报作为数据传输的载体。进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后再将数据报发送出去。

(3)相关操作类:

  •   DatagramPacket:表示数据报包,表示UDP通信中的数据单元。
  •   DatagramSocket:进行端到端通信的类。实现基于UDP的Socket通信。

(4)服务器端实现:

package function.socket.udp;

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

/**
 * UDP服务器端,实现基于UDP的用户登录
 *
 * @author kimtian
 */
public class UDPServer {
    /**
     * 服务器端实现方式:
     * 1.创建DatagranSocket,指定端口号。
     * 2.创建DatagramPacket。
     * 3.接收客户端发送的数据信息。
     * 4.读取数据。
     *
     * @param args
     */
    public static void main(String[] args) {
        try {
            //1.创建服务器端DatagranSocket,指定端口号。
            DatagramSocket datagramSocket = new DatagramSocket(9999);
            //2.创建数据报,用于接收客户端发送的数据
            //创建字节数组,指定接收的数据包的大小
            byte[] bytes = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
            //3.接收客户端发送的数据
            //在阻塞之前进行提示,服务器端已经启动
            System.out.println("******UDP服务器端已经启动,等待客户端发送数据******");
            //此方法在接收到数据报之前一直处于阻塞状态,等待接收客户端发送的数据
            datagramSocket.receive(datagramPacket);
            //4.读取数据
            //长度为接收到的长度datagramPacket.getLength()
            String info = new String(bytes, 0, datagramPacket.getLength());
            System.out.println("我是服务器,客户端说" + info);

            /**
             * 向客户端响应数据
             */
            //1.定义客户端的地址、端口号、数据
            InetAddress address = datagramPacket.getAddress();
            int port = datagramPacket.getPort();
            byte[] data = "欢迎您,客户端".getBytes();
            //2.创建数据报,包含响应的数据信息
            DatagramPacket datagramPacket1 = new DatagramPacket(data, data.length, address, port);
            //3.响应客户端
            datagramSocket.send(datagramPacket1);
            //4.关闭资源
            datagramSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(5)客户端实现:

package function.socket.udp;

import java.io.IOException;
import java.net.*;

/**
 * UDP客户端
 *
 * @author kimtian
 */
public class UDPClient {
    /**
     * 1.定义发送信息。
     * 2.创建DatagramPacket,包含将要发送的信息。
     * 3.创建DatagranSocket对象,实现数据对象的发送。
     * 4.发送数据。
     *
     * @param args
     */
    public static void main(String[] args) throws IOException {
        //1.定义服务器的地址、端口号、数据
        InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
        int port = 9999;
        byte[] bytes = "username:kimtian;password:123".getBytes();
        //2.创建数据报,包含发送的数据信息
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, inetAddress, port);
        //3.创建DatagramSocket对象
        DatagramSocket datagramSocket = new DatagramSocket();
        //4.向服务器端发送数据报
        datagramSocket.send(datagramPacket);
        /**
         * 接收服务器端响应信息
         */
        //1.创建数据报,用于接收服务器端响应的数据
        byte[] data = new byte[1024];
        DatagramPacket datagramPacket1 = new DatagramPacket(data, data.length);
        //2.接收服务器响应的数据
        datagramSocket.receive(datagramPacket1);
        //3.读取服务器端响应的数据信息
        String reply = new String(data, 0, datagramPacket1.getLength());
        System.out.println("我是客户端,服务器说:" + reply);
        //4.关闭资源
        datagramSocket.close();
    }
}

 (6)使用多线程实现多客户端之间的通信

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值