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

Java技术 潘老师 3年前 (2021-09-11) 1495 ℃ (0) 扫码查看

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

1、Java项目Socket编程案例

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

第1步:创建服务端

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

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();
}
}
}
该服务端主要监听本机的8877端口,记得处理完客户端消息后一定要关闭IO流和socket

第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项目

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

为了提高效率,我们可以将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中,具体如下:

<listener>
<listener-class>com.panziye.listener.SocketServerListener</listener-class>
</listener>

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

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


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/java/3623.html
喜欢 (3)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】