欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

JavaSocket编程(二)从单线程到多线程,实现聊天小程序

时间:2023-08-11
文章目录

单线程示例

实现功能

具体运行过程 代码

服务器端客户端封装的工具类 多线程示例思考 单线程示例 实现功能

服务端与客户端1对1聊天通信,需要先运行服务器端,后运行客户端,用户输入bye时,关闭socket,终止聊天。

具体运行过程 启动服务器端,创建ServerSocket,并阻塞于accept方法,等待客户端连接。启动客户端,连接服务器。服务器端接受连接,得到ServerSocket.accept( )返回的socket对象。服务器端和客户端分别创建各自监听消息的线程,之后可以通过socket的输入输出流进行信息交互。当某一方输入bye时,socket关闭,断开连接关闭相关资源,通信结束 代码 服务器端

//ServerDemo.javaimport java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(SocketUtils.PORT); Socket socket = serverSocket.accept(); //开启接收消息线程 new ReceiveMsg(socket).start(); //主线程开始发送消息 SocketUtils.sendMsg(socket, socket.getOutputStream()); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } }}

客户端

//ClientDemo.javaimport java.io.IOException;import java.net.InetAddress;import java.net.Socket;public class ClientDemo { public static void main(String[] args) { try { Socket socket = new Socket(InetAddress.getLocalHost(),SocketUtils.PORT); //接收消息线程开启 new ReceiveMsg(socket).start(); //主线程开始发送消息 SocketUtils.sendMsg(socket, socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } }}

封装的工具类

SocketUtils.java :封装了发送用户控制台输入消息的方法,以及定义了端口常量。

import java.io.IOException;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;import java.util.Scanner;public class SocketUtils { public static final int PORT = 8888; public static void sendMsg(Socket socket, OutputStream os){ PrintWriter pw = new PrintWriter(os); Scanner sc = new Scanner(System.in); String msg; while (!socket.isClosed() && (msg = sc.next())!=null){ // BufferedReader.readLine()方法直到遇到n或者r才会终止读取,是阻塞式方法 //手动封装消息,增加n pw.write(msg+'n'); pw.flush(); if ("bye".equals(msg)){ break; } } System.out.println("消息传输完毕,程序结束"); try { os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } }}

ReceiveMsg.java:创建新线程监听信息,并打印信息到控制台

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.Socket;import java.net.SocketException;public class ReceiveMsg extends Thread { InputStream inputStream; Socket socket; public ReceiveMsg(Socket socket) { this.socket = socket; try { this.inputStream = socket.getInputStream(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { System.out.println("线程被启动"); //字节流转字符流 InputStreamReader isr = new InputStreamReader(inputStream); //添加缓冲读数据 BufferedReader br = new BufferedReader(isr); try { String msg; //当收到消息时把消息打印到控制台 while ((msg=br.readLine())!=null) { System.out.println("msg:"+msg); if ("bye".equals(msg)){ System.out.println("接收消息socket关闭"); socket.close(); break; } } } catch (IOException e) { if (!(e instanceof SocketException)){ e.printStackTrace(); } } }}

多线程示例

使server可以支持多个client连接并聊天

只需要增加一个线程类,稍微改动一下ServerDemo.java

//ServerDemo.javaimport java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(SocketUtils.PORT); while (true){ Socket socket = serverSocket.accept(); // 开启新线程 循环监听连接请求 new ServerThread(socket).start(); }// new ReceiveMsg(socket).start();// SocketUtils.sendMsg(socket, socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } }}

增加的线程类ServerThread.java

import java.io.IOException;import java.net.Socket;public class ServerThread extends Thread{ Socket socket; public ServerThread(Socket socket) { this.socket = socket; } @Override public void run() { super.run(); //开启接收消息线程 new ReceiveMsg(socket).start(); //主线程开始发送消息 try { SocketUtils.sendMsg(socket, socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } }}

思考

不太明白的地方:

使用PrintWriter传输消息(例如从server向client传输消息),client的socket关闭之后,server发消息也不会抛出异常,不求甚解…

解决方法:server接收到bye后关闭socket,并在发送消息判断socket.isClosed()

但同时这又会带来一个问题,单线程进行聊天时,可以注意发送消息的这一段代码

while (!socket.isClosed() && (msg = sc.next())!=null){pw.write(msg+'n');pw.flush();if ("bye".equals(msg)){ break;}}

当server启动之后,会阻塞于accept方法,client启动后,双方的代码都会阻塞在sc.next( ),socket.isClosed() 返回的是false。这时,任意一方输入bye关闭socket,另外一方还需要输入一行才能彻底结束。

可能可行的解决方法:通过注册–通知机制实现事件监听(观察者模式),达到实时监听socket是否关闭,还有待实现和验证…

此外,还存在一个问题,当使用多线程时,会造成聊天混乱的情况,两个连接client的线程并发运行,会导致server需要交替输入。

参考资料:

Scanner 类抛出java.util.NoSuchElementException

java 线程监听事件_java中的事件监听是怎样实现随时监听的,是通过线程吗

java-用java.net.Socket和java.net.ServerSocket实现简单的聊天程序

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。