Java Socket编程例子并应用到Java Web项目

IT 文章4年前 (2021)发布 小编
0 0 0

最近Java潘老师在协同底层项目开发web端需要使用到socket与其通信,实现消息传输,于是重拾那遗忘久矣的Java Socket网络编程技术。还记得当时学习Java Socket编程例子的时候是写了一个在线聊天室,有单聊有群聊,玩的的不亦乐乎,真正能体会到学Java技术原来这么有意思,不过今天潘老师就总结下最基础入门的Java Socket编程案例,并写把它融入到Java Web项目中,注意不是websocket哦~

1、Java项目Socket编程案例

我们目前的需求很简单,就是新建一个Java项目,然后有客户端,有服务端,客户端可以给服务端发消息,服务端收到消息然后返回一个响应给客户端。本案例基于TCP协议,如果想使用UDP的可以自行百度去哦。

第1步:创建服务端

由于服务端中有个accept方法是阻塞方法,用来监听端口等待客户端发送消息,所以我们服务端要单独开一个线程去监听端口,否则运行时会阻塞主线程,导致程序堵死,下面就是潘老师写的一个简单的服务端代码:

ad

程序员导航

优网导航旗下整合全网优质开发资源,一站式IT编程学习与工具大全网站

package com.panziye.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server extends Thread {
	// 服务端对象
	private ServerSocket serverSocket;
	// 默认监听8877端口
	private int port = 8877;

	// 构造方法,初始化服务端
	public Server() {
		try {
			// 创建Socket服务器对象,监听8877端口
			serverSocket = new ServerSocket(port);
			System.out.println("ServerSocket创建了....");
		} catch (Exception e) {
			System.out.println("ServerSocket创建出错....");
		}

	}

	// 重写run方法
	public void run() {
		System.out.println("服务端启动了,等待客户端发送消息....");
		// 循环监听,直到线程中断为止
		while(!this.isInterrupted()){
			try {
				// accept是阻塞方法,等待客户端发消息
				Socket socket = serverSocket.accept();
				if(socket != null && !socket.isClosed()){
					// 处理socket消息
					handleSocket(socket);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	// 处理服务端接收到的socket消息
	private void handleSocket(Socket socket) {
		InputStream is = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		OutputStream os = null;
		PrintWriter pw = null;
		StringBuffer result = new StringBuffer();
		try {
			is = socket.getInputStream();
			isr = new InputStreamReader(is);
			br = new BufferedReader(isr);

			String info = null;
			// 从流中读取客户端消息
			while ((info = br.readLine()) != null) {
				result.append(info);
			}
			// 输出消息
			System.out.println("我是服务器,客户端说:" + result);
			socket.shutdownInput();
			// 给客户端响应消息
			os = socket.getOutputStream();
			pw = new PrintWriter(os);
			pw.write("客户端你好,欢迎访问潘老师博客:https://www.panziye.com");

			pw.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭资源
			try {
				if (pw != null)
					pw.close();
				if (os != null)
					os.close();
				if (br != null)
					br.close();
				if (isr != null)
					isr.close();
				if (is != null)
					is.close();
				if (socket != null)
					socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

    /**
     * 关闭socket
     */
    public void closeSocketServer(){
        try {
            if(serverSocket != null && !serverSocket.isClosed()){
                serverSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

[v_blue]该服务端主要监听本机的8877端口,记得处理完客户端消息后一定要关闭IO流和socket[/v_blue]

第2步:创建客户端

客户端比较简单,主要就是和服务端建立连接,然后可以像服务端发送消息,并获取服务端返回的消息。例子代码如下:

package com.panziye.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {
	// 主机地址
	private String host = "localhost";
	// 端口
	private int port = 8877;

	/**
	 * 客户端向服务端发送消息
	 */
	public String sendMessage(String msg) {
		// 响应结果
		StringBuffer result = new StringBuffer();
		BufferedReader br = null;
		InputStream is = null;
		OutputStream os = null;
		PrintWriter pw = null;
		Socket socket = null;
		try {
			// 和服务器创建连接
			socket = new Socket(host,port);
			System.out.println("和服务器已建立连接....");
			// 要发送给服务器的信息
			os = socket.getOutputStream();
			pw = new PrintWriter(os);
			// 给服务端发msg
			pw.write(msg);
			pw.flush();

			socket.shutdownOutput();

			// 从服务器接收的信息
			is = socket.getInputStream();
			br = new BufferedReader(new InputStreamReader(is));
			String info = null;
			while((info = br.readLine())!=null){
				result.append(info);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			// 关闭流和socket
			try {
				if(br != null)
					br.close();
				if(is != null)
					is.close();
				if(os != null)
					os.close();
				if(pw != null)
					pw.close();
				if(socket != null)
					socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result.toString();
	}
}

第3步:创建测试类

最后我们创建一个测试类,来测试效果,具体代码如下:

package com.panziye.socket;

public class SocketTest {
	public static void main(String[] args) {
		// 创建服务端线程并启动
		new Server().start();
		// 休眠3s等待服务端线程启动成功
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 创建客户端并发消息
		String response = new Client().sendMessage("你好潘老师...");
		System.out.println("服务端响应:"+response);
	}

}

最后我们运行,发现可以成功建立连接,发送消息,获得响应。查看控制台打印结果具体如图:
Java Socket编程例子并应用到Java Web项目

ad

AI 工具导航

优网导航旗下AI工具导航,精选全球千款优质 AI 工具集

优化:使用线程池去异步处理

为了提高效率,我们可以将handleSocket方法也封装在一个Runnable线程中,然后可以每次accept到socket消息时就,交给线程池去处理即可,这样就可以避免阻塞情况,具体修改的代码如下。

1)新增HandleSocketRunnable线程

package light.mvc.thread;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class HandleSocketRunnable implements Runnable {

	private Socket socket = null;
	private InputStream is=null;
	private InputStreamReader isr=null;
	private BufferedReader br=null;
	private OutputStream os=null;
	private PrintWriter pw=null;

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

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName());
		// 获取消息
		String message = getMessage();
		// 处理message
		System.out.println(message);
		// 发送响应
		boolean flag= responseMessage("响应消息");
		// 关闭socket
		closeSocket();
	}

	// 获取客户端消息
	private String getMessage() {

        StringBuffer result = new StringBuffer();
        try {
			is = socket.getInputStream();
			isr = new InputStreamReader(is);
			br = new BufferedReader(isr);
			String info = null;
			while((info=br.readLine())!=null){
				result.append(info);
			}
			socket.shutdownInput();
        } catch (Exception e) {
			e.printStackTrace();
		}
        return result.toString();
	}
	// 给客户端返回响应
	private boolean responseMessage(String message) {
        OutputStream os=null;
        PrintWriter pw=null;
        try {
			os = socket.getOutputStream();
			pw = new PrintWriter(os);
			pw.write(message);
			pw.flush();
        } catch (Exception e) {
			e.printStackTrace();
			 return false;
		}
        return true;
	}

	// 关闭socket
	private void closeSocket() {
		//关闭资源
		try {
			if(br!=null)
				br.close();
			if(isr!=null)
				isr.close();
			if(is!=null)
				is.close();
			if(pw!=null)
				pw.close();
			if(os!=null)
				os.close();
			if(socket != null)
				socket.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	}
}

2)修改Server线程类

package com.panziye.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server extends Thread {
	// 服务端对象
	private ServerSocket serverSocket;
	// 创建定长线程池
	private ExecutorService executor = Executors.newFixedThreadPool(5);
	// 默认监听8877端口
	private int port = 8877;

	// 构造方法,初始化服务端
	public Server() {
		try {
			// 创建Socket服务器对象,监听8877端口
			serverSocket = new ServerSocket(port);
			System.out.println("ServerSocket创建了....");
		} catch (Exception e) {
			System.out.println("ServerSocket创建出错....");
		}

	}

	// 重写run方法
	public void run() {
		System.out.println("服务端启动了,等待客户端发送消息....");
		// 循环监听,直到线程中断为止
		while(!this.isInterrupted()){
			try {
				// accept是阻塞方法,等待客户端发消息
				Socket socket = serverSocket.accept();
				if(socket != null && !socket.isClosed()){
					// 线程池多线程处理接收的数据
                			executor.submit(new HandleSocketRunnable(socket));
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

    /**
     * 关闭socket
     */
    public void closeSocketServer(){
        try {
            if(serverSocket != null && !serverSocket.isClosed()){
                serverSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

通过以上的代码修改我们就可以实现使用线程池去处理socket请求了,效率也会随之提高很多。

2、Socket编程案例应用到Java web项目

其实上面的代码如果你都理解了,那我们可以很轻松地将该案例应用到java web项目中,而难点就在于如何使服务端ServerThread随着web项目启动而启动。这里以Spring项目为例。

第1步:创建监听器

这里我们可以在Spring项目中新建一个监听器,实现ServletContextListener主要重写容器销毁和容器初始化方法:

package com.panziye.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.panziye.socket.ServerThread;

public class SocketServerListener implements ServletContextListener {

	private ServerThread serverThread;

	/**
	 * 销毁方法
	 */
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		if (serverThread != null && serverThread.isInterrupted()) {
			serverThread.interrupt();
        }
	}

	/**
	 * 初始化方法
	 */
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		if(serverThread == null) {
			// 创建线程
			ServerThread serverThread= new ServerThread (null);
			// 设为守护线程
			serverThread.setDaemon(true);
			// 启动socket线程
			serverThread.start();
		}

	}
}

第2步:配置web.xml

我们需要将创建好的监听器配置在web.xml中,具体如下:


    com.panziye.listener.SocketServerListener

最后启动项目就会发现,Socket服务端线程随着项目启动而启动了。

ad

免费在线工具导航

优网导航旗下整合全网优质免费、免注册的在线工具导航大全

好了,以上就是Java Socket编程例子并应用到了Java Web项目中,其实里面的代码还有许多地方值得优化,你都学会了吗?

© 版权声明

相关文章

暂无评论

暂无评论...