Java Socket 编程


网络编程简介

网络编程:指在网络通信协议下,不同计算机上运行的程序,可以进行数据传输。

网络编程三要素

  1. IP 地址:网络中设备的唯一标识。
  2. 端口号:设备上应用程序的唯一标识。(用两个字节表示的整数,它的取值范围是 065535。其中,01023 之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用 1024 以上的端口号。)
  3. 协议:它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有 UDP 协议和 TCP 协议。

更多网络知识请参考本博客的网络系列文章


InetAddress:IP 地址类

InetAddress 类表示 Internet协议(IP)地址。

相关方法

方法名 说明
static InetAddress getByName(String host) 确定主机名称的 IP 地址。主机名称可以是机器名称,也可以是 IP 地址
String getHostName() 获取此 IP 地址的主机名
String getHostAddress() 返回文本显示中的 IP 地址字符串

代码示例

import java.net.InetAddress;

public class InetAddressDemo {
    public static void main(String[] args) throws UnknownHostException {
        // InetAddress address = InetAddress.getByName("juno");
        InetAddress address = InetAddress.getByName("192.168.1.66");

        //public String getHostName():获取此IP地址的主机名
        String name = address.getHostName();
        //public String getHostAddress():返回文本显示中的IP地址字符串
        String ip = address.getHostAddress();

        System.out.println("主机名:" + name);
        System.out.println("IP地址:" + ip);
    }
}

UDP 编程

发送端

构造方法

方法名 说明
DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任何可用端口
DatagramPacket(byte[] buf, int len, InetAddress add, int port) 创建数据包,发送长度为 len 的数据包到指定主机的指定端口

相关方法

方法名 说明
void send(DatagramPacket p) 发送数据报包
void close() 关闭数据报套接字
void receive(DatagramPacket p) 从此套接字接受数据报包

发送数据的步骤

  1. 创建发送端的 Socket 对象(DatagramSocket);
  2. 创建数据,并把数据打包;
  3. 调用 DatagramSocket 对象的方法发送数据;
  4. 关闭发送端。

代码示例

public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 1. 创建发送端的Socket对象(DatagramSocket)
        // DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口
        DatagramSocket ds = new DatagramSocket();

        // 2. 创建数据,并把数据打包
        // DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        // 构造一个数据包,发送长度为 length的数据包到指定主机上的指定端口号。
        byte[] bys = "hello,udp,我来了".getBytes();

        DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("127.0.0.1"),10086);

        // 3. 调用DatagramSocket对象的方法发送数据
        //void send(DatagramPacket p) 从此套接字发送数据报包
        ds.send(dp);

        // 4. 关闭发送端
        // void close() 关闭此数据报套接字
        ds.close();
    }
}

接收端

构造方法

方法名 说明
DatagramPacket(byte[] buf, int len) 创建一个 DatagramPacket 对象用于接收长度为 len 的数据包

相关方法

方法名 说明
byte[] getData() 返回数据缓冲区
int getLength() 返回要发送的数据的长度或接收的数据的长度

接收数据的步骤

  1. 创建接收端的 Socket 对象(DatagramSocket);
  2. 创建一个数据包,用于接收数据;
  3. 调用 DatagramSocket 对象的方法接收数据;
  4. 解析数据包,并把数据在控制台显示;
  5. 关闭接收端。

代码示例

public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 创建接收端的Socket对象(DatagramSocket)
        DatagramSocket ds = new DatagramSocket(12345);

        // 创建一个数据包,用于接收数据
        byte[] bys = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bys, bys.length);

        // 调用DatagramSocket对象的方法接收数据
        ds.receive(dp);

        // 解析数据包,并把数据在控制台显示
        System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
        }
    }
}

TPC 编程

客户端

Java 为客户端提供了 Socket 类,为服务器端提供了 ServerSocket 类。

构造方法

方法名 说明
Socket(InetAddress address, int port) 创建流套接字并将其连接到指定 IP 指定端口号
Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号

相关方法

方法名 说明
InputStream getInputStream() 返回此套接字的输入流
OutputStream getOutputStream() 返回此套接字的输出流

示例代码

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端的Socket对象(Socket)
        // Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
        Socket s = new Socket("127.0.0.1", 10000);

        // 获取输出流,写数据
        // OutputStream getOutputStream() 返回此套接字的输出流
        OutputStream os = s.getOutputStream();
        os.write("hello,tcp,我来了".getBytes());

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

服务端

构造方法

方法名 说明
ServletSocket(int port) 创建绑定到指定端口的服务器套接字

相关方法

方法名 说明
Socket accept() 监听要连接到此的套接字并接受它

注意事项

  • accept 方法是阻塞的,作用就是等待客户端连接。
  • read 方法也是阻塞的。

示例代码

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器端的Socket对象(ServerSocket)
        // ServerSocket(int port):创建绑定到指定端口的服务器套接字
        ServerSocket ss = new ServerSocket(10000);

        // Socket accept():监听要连接到此套接字并接受它
        Socket s = ss.accept();

        // 获取输入流,读数据,并把数据显示在控制台
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        String data = new String(bys,0,len);
        System.out.println("数据是:" + data);

        // 释放资源
        s.close();
        ss.close();
    }
}

案例:1V1 聊天

案例需求

  • 客户端:发送数据,接受服务器反馈。

  • 服务器:收到消息后给出反馈。

案例分析

  • 客户端创建对象,使用输出流输出数据。
  • 服务端创建对象,使用输入流接受数据。
  • 服务端使用输出流给出反馈数据。
  • 客户端使用输入流接受反馈数据。

代码实现

// 客户端
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 10000);

        OutputStream os = socket.getOutputStream();
        os.write("hello".getBytes());
        // os.close(); 如果在这里关流,会导致整个 socket 都无法使用
        socket.shutdownOutput();  // 仅仅关闭输出流,并会写一个结束标记,对socket没有任何影响
        
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line;
        while((line = br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
        os.close();
        socket.close();
    }
}

// 服务端
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10000);

        Socket accept = ss.accept();

        InputStream is = accept.getInputStream();
        int b;
        while((b = is.read())!=-1){
            System.out.println((char) b);
        }

        System.out.println("看看我执行了吗?");

        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write("你谁啊?");
        bw.newLine();
        bw.flush();

        bw.close();
        is.close();
        accept.close();
        ss.close();
    }
}

案例:文件上传

案例需求

  • 客户端:数据来自于本地文件,接收服务器反馈。

  • 服务器:接收到的数据写入本地文件,给出反馈。

案例分析

  • 创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用 shutdownOutput() 方法告知服务端传输结束。
  • 创建服务器对象,创建输出流对象指向文件,每接受一次数据就使用输出流输出到文件中,传输结束后。使用输出流给客户端反馈信息。
  • 客户端接受服务端的回馈信息。

相关方法

方法名 说明
void shutdownInput() 将此套接字的输入流放置在“流的末尾”
void shutdownOutput() 禁止用此套接字的输出流

代码实现

// 客户端
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",10000);

        // 本地流,用来读取本地文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("socketmodule\\ClientDir\\1.jpg"));

        // 写到服务器 --- 网络中的流
        OutputStream os = socket.getOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(os);

        int b;
        while((b = bis.read())!=-1){
            bos.write(b);  // 通过网络写到服务器中
        }
        bos.flush();
        // 给服务器一个结束标记,告诉服务器文件已经传输完毕
        socket.shutdownOutput();

        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line;
        while((line = br.readLine()) !=null){
            System.out.println(line);
        }
        bis.close();
        socket.close();
    }
}

// 服务端
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10000);

        Socket accept = ss.accept();

        // 网络流,从客户端读取数据
        BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
        // 本地流,把数据写到本地中,实现持久化存储
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("socketmodule\\ServerDir\\copy.jpg"));

        int b;
        while((b = bis.read()) !=-1){
            bos.write(b);
        }

        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        bos.close();
        accept.close();
        ss.close();
    }
}
posted @ 2021-12-20 12:57  Juno3550  阅读(103)  评论(0编辑  收藏  举报