Java 网络IO编程

什么是Socket

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

如下图所示

Java 网络IO编程_第1张图片

我们可以选择基于TCP或UDP协议进行网络通信

1. 基于TCP协议进行网络通信

通过TCP发送数据时,首先要创建连接。建立TCP连接后,TCP保证数据到达另一端,或者它会告诉我们发生了错误。

1.1 客户端

为了实现通过TCP协议连接到服务器,需要创建一个java.net.Socket并将其连接到服务器。

1.1.1 创建Socket

如下为连接到ip为127.0.0.1,端口号为8000的服务器。ip地址也可以为域名。

 Socket socket = new Socket("127.0.0.1", 8000);

1.1.2 将数据写到Socket

socket.getOutputStream().write("hello world".getBytes());

1.1.3 从Socket读取数据

InputStream inputStream = socket.getInputStream();
    while ((len = inputStream.read(data)) != -1) {
        System.out.println(new String(data, 0, len));
    }
  • 注意:在读取文件时,不能通过返回-1来判断是否读完这个文件。因为只有在服务器关闭的情况下连接才返回-1。

1.1.4 关闭Socket

socket.close();

完整代码如下:

@Slf4j
public class TCPClient {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        new Thread(() -> {
            try {
                Socket socket = new Socket("127.0.0.1", 8000);
                for (int i= 0;i<100;i++) {
                    try {
                        socket.getOutputStream().write((new Date() + ": hello world").getBytes());
                        log.info("client发送消息");
                    } catch (Exception e) {
                    }
                }
            } catch (IOException e) {
            }
        }).start();

        countDownLatch.await();
    }
}

1.2 服务端

为了实现通过TCP协议来监听客户端传入Java服务器的连接,需要使用到java.net.ServerSocket,当客户端通过客户端套接字连接到服务器的ServerSocket时,服务器上会为该连接分配一个Socket。客户端和服务器通过Socket-to-Socket方式通信。

1.2.1 创建ServerSocket

如下,创建一个ServerSocket监听端口8000。

ServerSocket serverSocket = new ServerSocket(8000);

1.2.2 监听传入的连接

while(true){
Socket socket = serverSocket.accept();
//todo:连接后的操作
}
  • 注意:只有当前服务线程调用accept()方法时,客户端的连接才能被接收,除此之外的任何时间都没有客户端可以连接到服务器。所以在接收连接后,一般都将Socket放到工作线程执行后续逻辑。

1.2.3 关闭客户端Socket

当客户端的请求完成,并且不会从该客户端收到进一步的请求,则需要将Socket关闭。

socket.close();

1.2.4 关闭服务器端 Socket

如果有需要关闭服务:

serverSocket.close();

完整代码如下:

@Slf4j
public class TCPServer {

    public static void main(String[] args) throws Exception {

        CountDownLatch countDownLatch = new CountDownLatch(1);
        ServerSocket serverSocket = new ServerSocket(8000);

        // (1) 接收新连接线程
        new Thread(() -> {
            while (true) {
                try {
                    // (1) 阻塞方法获取新的连接
                    Socket socket = serverSocket.accept();
                    log.info("service接收消息");
                    // (2) 每一个新的连接都创建一个线程,负责读取数据
                    new Thread(() -> {
                        log.info("创建线程");
                        try {
                            int len;
                            byte[] data = new byte[1024];
                            InputStream inputStream = socket.getInputStream();
                            // (3) 按字节流方式读取数据
                            while ((len = inputStream.read(data)) != -1) {
                                log.info(new String(data, 0, len));
                            }
                        } catch (IOException e) {
                        }
                    }).start();

                } catch (IOException e) {
                }

            }
        }).start();

        countDownLatch.await();
    }
}

2. 基于UDP协议进行网络通信

使用UDP协议,客户端和服务器之间没有连接。客户端可以向服务器发送数据,并且服务器可以(或可以不)接收该数据。客户端永远不会知道数据是否在另一端收到。服务器发送数据到客户端也是如此。

UDP和TCP端口不一样。计算机可以具有不同的进程,例如在UDP和TCP中同时侦听端口8000。

2.1 客户端

2.1.1 创建DatagramSocket

DatagramSocket datagramSocket = new DatagramSocket();

2.1.2 创建DatagramPacket

参数data为此次通信需要发送的数据,inetAddress为此次通信的节点地址的ip信息,最后一个参数为端口号。

InetAddress inetAddress = InetAddress.getLocalHost();
byte[] data = "123123123".getBytes();
DatagramPacket datagramPacket = new DatagramPacket(data,data.length,inetAddress,8000);

2.1.3 发送数据

datagramSocket.send(datagramPacket);

完整代码如下:

public class UDPClient {

    private static DatagramPacket datagramPacket;
    private static InetAddress inetAddress;
    private static DatagramSocket datagramSocket;

    public static void main(String[] args) throws IOException, InterruptedException {
        inetAddress = InetAddress.getLocalHost();
        datagramSocket = new DatagramSocket();
        while (true){
            byte[] data = "123123123".getBytes();
            datagramPacket = new DatagramPacket(data,data.length,inetAddress,8000);
            datagramSocket.send(datagramPacket);
            Thread.sleep(2000);
        }

    }
}

2.2 服务端

2.2.1 创建DatagramSocket

监听8000端口

DatagramSocket datagramSocket = new DatagramSocket(8000);

2.2.2 创建DatagramPacket

创建一个缓冲区,接收客户端发送的数据

byte[] data = new byte[9];
datagramPacket = new DatagramPacket(data,data.length);

2.2.3 接收数据

接收的数据为byte数组

byte[] receive = datagramPacket.getData();
System.out.println(new String(receive));

完整代码如下:

public class UDPServer {

    private static DatagramSocket datagramSocket;
    private static DatagramPacket datagramPacket;

    public static void main(String[] args) throws IOException, InterruptedException {
        //UDP和TCP端口不一样。计算机可以具有不同的进程,例如在UDP和TCP中同时侦听端口80。
        datagramSocket = new DatagramSocket(8000);
        while (true){
            byte[] data = new byte[9];
            datagramPacket = new DatagramPacket(data,data.length);
            datagramSocket.receive(datagramPacket);
            byte[] receive = datagramPacket.getData();
            System.out.println(new String(receive));
        }

    }
}

后记

Java IO编程,每建立一个连接,就需要提供一个单独的线程从流中读取数据。尽管可以把事件放到线程池中处理,但当并发量很大的时候,系统开销仍然是支撑不住的。

参考链接:
Jenkov.com
图片来源:

一碗清水CSDN

你可能感兴趣的:(io,java,socket,io编程)