socket+线程池,写服务端和客户端进行交互

以下内容转自:
https://www.cnblogs.com/gnoc/p/4866788.html

前言  
socket(套接字),Socket和ServerSocket位于java.net包中,持续开启服务端,接收来自客户端的信息,并响应。

最开始,咱们先来两段最简单的服务端和客户端的代码

最简单的服务端代码:

package com.socket;

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


public class TestSocketService {
//自定义一个端口号
    private static final int PORT = 8888;

    public static void main(String[] args) {

        ServerSocket server = null;
        Socket socket = null;
        DataInputStream dataInputStream = null;
        DataOutputStream dataOutputStream = null;
        try {
            server = new ServerSocket(PORT);
            System.out.println("监听端口:" + PORT);
            socket = server.accept();

            // 接受客户端请求
            dataInputStream = new DataInputStream(socket.getInputStream());
            String request = dataInputStream.readUTF();
            System.out.println("from client..." + request);

            // 响应客户端
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            String response = "收到";
            dataOutputStream.writeUTF(response);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (dataInputStream != null) {
                    dataInputStream.close();
                }
                if (dataOutputStream != null) {
                    dataOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

最简单的客户端代码:

package com.socket;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;


public class TestSocketClient {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 8888;

    public static void main(String[] args) {

        Socket socket = null;
        DataInputStream dataInputStream = null;
        DataOutputStream dataOutputStream = null;
        try {
            socket = new Socket(HOST, PORT);

            //给服务端发送请求
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            String request = "我是客户1";
            dataOutputStream.writeUTF(request);

            dataInputStream = new DataInputStream(socket.getInputStream());
            String response = dataInputStream.readUTF();
            System.out.println(response);

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                if(dataInputStream != null){
                    dataInputStream.close();
                }
                if(dataOutputStream != null){
                    dataOutputStream.close();
                }
                if(socket != null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端和服务端分别运行,能收发数据,但是服务端只服务了一次就停止了,这明显不符合需求,服务端应该响应完客户端之后,继续监听端口,等待下一个客户端的连接。

让服务端一直提供服务
将服务端的代码写入循环中持续循环,一直监听来自客户端的请求。修改服务端的代码:

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

        ServerSocket server = null;
        Socket socket = null;
        DataInputStream dataInputStream = null;
        DataOutputStream dataOutputStream = null;
        server = new ServerSocket(PORT);
        System.out.println("监听端口:" + PORT);
        while(true){
            try {
                socket = server.accept();

                // 接受客户端请求
                dataInputStream = new DataInputStream(socket.getInputStream());
                String request = dataInputStream.readUTF();
                System.out.println("from client..." + request);

                // 响应客户端
                dataOutputStream = new DataOutputStream(socket.getOutputStream());
                String response = "收到";
                dataOutputStream.writeUTF(response);

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (dataInputStream != null) {
                        dataInputStream.close();
                    }
                    if (dataOutputStream != null) {
                        dataOutputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

通过while(true)循环,服务端一直监听端口,由于socket是阻塞的,只有服务端完成了当前客户端的响应,才会继续处理下一个客户端的响应。这样一直让主线线程去处理socket请求不合适,因此需要为服务端加上多线程功能,同时处理多个socket请求。

给服务端加上多线程
修改代码,将服务端的socket处理抽取出来,并且封装到Runnable接口的run方法中:

package com.socket;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;


public class TestThread extends Thread {

    private Socket socket;

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

    public void run() {

        DataInputStream dataInputStream = null;
        DataOutputStream dataOutputStream = null;
        try {
            // 接受客户端请求
            dataInputStream = new DataInputStream(socket.getInputStream());
            String request = dataInputStream.readUTF();
            System.out.println("from client..." + request+" 当前线程:"+Thread.currentThread().getName());

            // 响应客户端
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            String response = "收到";
            dataOutputStream.writeUTF(response);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (dataInputStream != null) {
                    dataInputStream.close();
                }
                if (dataOutputStream != null) {
                    dataOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

修改服务端,添加多线程功能:

package com.socket;

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


public class TestSocketService {

    private static final int PORT = 8888;

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

        ServerSocket server = null;
        Socket socket = null;
        server = new ServerSocket(PORT);
        System.out.println("监听端口:" + PORT);
        while(true){
                socket = server.accept();
                new TestThread(socket).start();
        }
    }
}

弊端分析

尽管服务端现在已经有了多线程处理能力,但是服务端每次接收到客户端的请求后,都会创建一个新的线程去处理,而jvm的线程数量过多是,服务端处理速度会变慢。而且如果并发较高的话,瞬间产生的线程数量也会比较大,因此,我们需要再给服务端加上线程池的功能。

使用java.util.concurrent.Executor类就可以创建一个简单的线程池,代码如下:

package com.socket;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;


public class TestSocketService {

    private static final int PORT = 8888;

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

        ServerSocket server = null;
        Socket socket = null;
        server = new ServerSocket(PORT);
        System.out.println("监听端口:" + PORT);

        //FixedThreadPool最多开启3(参数)个线程,多余的线程会存储在队列中,等线程处理完了
        //再从队列中获取线程继续处理
        Executor executor = Executors.newFixedThreadPool(3);
        while(true){
                socket = server.accept();
                executor.execute(new TestThread(socket));
        }
    }
}

Executor一共有4种线程池实现,这里使用了FixedThreadPool最多开启3(参数)个线程,多余的线程会存储在队列中,等线程处理完了再从队列中获取,继续处理。这样的话无论并发量多大,服务端只会最多3个线程进行同时处理,使服务端的压力不会那么大。

猜你喜欢

转载自blog.csdn.net/weixin_43020130/article/details/82255514