JAVA socket聊天室程序 readLine()阻塞

JAVA socket聊天室程序 readLine()阻塞,第1张

对每一个客户端都建立一个线程来接收消息,发消息与接消息不要在同一线程上,那就才能解决阻塞问题。一般新手用socket编程,大都是遇到阻塞问题不懂解决,导致了收发消息失败。其实网上socket聊天通讯的例子大把,遇到问题查查看,思考下就能明白了。

有问题欢迎提问,满意请采纳,thx.

package API_Day09

import java.io.BufferedReader

import java.io.IOException

import java.io.InputStream

import java.io.InputStreamReader

import java.io.OutputStream

import java.io.OutputStreamWriter

import java.io.PrintWriter

import java.net.Socket

import java.util.Scanner

/**

* 控制台聊天程序

* 客户端应用程序

* @author Jacob

*

*/

public class chatClient

{

//客户端用于与服务端连接的Socket

private Socket clientSocket

/**

* 构造方法,客户端初始化

*/

public chatClient()

{

try

{

/*

* socket(String host, int port)

* 地址: IP地址,用来定位网络上的计算机

* 端口: 用来找到远端计算机上用来连接的服务端应用程序

*/

clientSocket = new Socket("localhost",12580)

}

catch (Exception e)

{

e.printStackTrace()

}

}

/**

* 客户端昵称验证方法

* @param 为Scanner

*/

private void inputNickName(Scanner scan) throws Exception

{

String nickName = null

//创建输出流

PrintWriter pw = new PrintWriter(

new OutputStreamWriter(clientSocket.getOutputStream(),

"UTF-8"),true)

//创建输入流

BufferedReader br = new BufferedReader(

new InputStreamReader(

clientSocket.getInputStream(),"UTF-8"))

while(true)

{

System.out.println("请创建您的昵称:")

nickName = scan.nextLine()

if (nickName.trim().equals(""))

{

System.out.println("昵称不得为空")

}

else

{

pw.println(nickName)

String pass = br.readLine()

if(pass!=null&&!pass.equals("OK"))

{

System.out.println("昵称已经被占用,请更换!")

}

else

{

System.out.println("你好!"+nickName+"可以开始聊天了")

break

}

}

}

}

/*

* 客户端启动的方法

*/

public void start()

{

try

{

/*

* 创建Scanner,读取用户输入内容

* 目的是设置客户端的昵称

*/

Scanner scanner = new Scanner(System.in)

inputNickName(scanner)

/*

* 将用于接收服务器端发送过来的信息的线程启动

*/

Runnable run = new GetServerMsgHandler()

Thread t = new Thread(run)

t.start()

/*

* 建立输出流,给服务端发信息

*/

OutputStream os = clientSocket.getOutputStream()

OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8")

PrintWriter pw = new PrintWriter(osw,true)

while(true)

{

pw.println(scanner.nextLine())

}

}

catch(Exception e)

{

e.printStackTrace()

}

finally

{

if(clientSocket !=null)

{

try

{

clientSocket.close()

}

catch(IOException e)

{

e.printStackTrace()

}

}

}

}

/**

* 该线程体用来循环读取服务端发送过来的信息

* 并输出到客户端的控制台

* @param args

*/

class GetServerMsgHandler implements Runnable

{

@Override

public void run()

{

try

{

InputStream is = clientSocket.getInputStream()

InputStreamReader isr = new InputStreamReader(is,"UTF-8")

BufferedReader br = new BufferedReader(isr)

String msgString = null

while((msgString = br.readLine())!= null)

{

System.out.println("服务端提示:"+ msgString)

}

}

catch(Exception e)

{

e.printStackTrace()

}

}

}

public static void main(String[] args)

{

chatClient client = new chatClient()

client.start()

}

}

package API_Day09

import java.io.BufferedReader

import java.io.IOException

import java.io.InputStream

import java.io.InputStreamReader

import java.io.OutputStream

import java.io.OutputStreamWriter

import java.io.PrintWriter

import java.net.ServerSocket

import java.net.Socket

import java.util.HashMap

import java.util.Map

import java.util.concurrent.ExecutorService

import java.util.concurrent.Executors

/**

* 控制台聊天程序

* 服务端应用程序

* @author Jacob

*

*/

public class chatServer

{

/**

* ServerSocket 是运行在服务端的Socket

* 用来监听端口,等待客户端的连接,

* 一旦连接成功就会返回与该客户端通信的Socket

*/

private ServerSocket serverSocket

/**

* 创建线程池来管理客户端的连接线程

* 避免系统资源过度浪费

*/

private ExecutorService threadPool

/**

* 该属性用来存放客户端之间私聊的信息

*/

private Map<String,PrintWriter>allOut

/**

* 构造方法,服务端初始化

*/

public chatServer()

{

try

{

/*

* 创建ServerSocket,并申请服务端口

* 将来客户端就是通过该端口连接服务端程序的

*/

serverSocket = new ServerSocket(12580)

/*

* 初始化Map集合,存放客户端信息

*/

allOut = new HashMap<String, PrintWriter>()

/*

* 初始化线程池,设置线程的数量

*/

threadPool = Executors.newFixedThreadPool(10)

/*

* 初始化用来存放客户端输出流的集合,

* 每当一个客户端连接,就会将该客户端的输出流存入该集合;

* 每当一个客户端断开连接,就会将集合中该客户端的输出流删除;

* 每当转发一条信息,就要遍历集合中的所有输出流(元素)

* 因此转发的频率高于客户端登入登出的频率,

* 还是应该使用ArrayList来存储元素,仅限群聊,私聊不行

* allOut = new ArrayList<PrintWriter>()

*/

}

catch (Exception e)

{

e.printStackTrace()

}

}

/*

* 将客户端的信息以Map形式存入集合中

*/

private void addOut(String key,PrintWriter value)

{

synchronized(this)

{

allOut.put(key, value)

}

}

/*

* 将给定的输出流从共享集合中删除

* 参数为客户端nickName,作为Map的key键

*/

private synchronized void removeOut(String key)

{

allOut.remove(key)

System.out.println("当前在线人数为:"+ allOut.size())

}

/*

* 将给定的消息转发给所有客户端

*/

private synchronized void sendMsgToAll(String message)

{

for(PrintWriter out: allOut.values())

{

out.println(message)

System.out.println("当前在线人数为:"+ allOut.size())

}

}

/*

* 将给定的消息转发给私聊的客户端

*/

private synchronized void sendMsgToPrivate(String nickname,String message)

{

PrintWriter pw = allOut.get(nickname)//将对应客户端的聊天信息取出作为私聊内容发送出去

if(pw!=null)

{

pw.println(message)

System.out.println("当前在线私聊人数为:"+ allOut.size())

}

}

/**

* 服务端启动的方法

*/

public void start()

{

try

{

while(true)

{

/*

* 监听10086端口

*/

System.out.println("等待客户端连接... ... ")

/*

* Socket accept() 这是一个阻塞方法,会一直在10086端口进行监听

* 直到一个客户端连接上,此时该方法会将与这个客户端进行通信的Socket返回

*/

Socket socket = serverSocket.accept()

System.out.println("客户端连接成功! ")

/*

* 启动一个线程,由线程来处理客户端的请求,这样可以再次监听

* 下一个客户端的连接了

*/

Runnable run = new GetClientMsgHandler(socket)

threadPool.execute(run)//通过线程池来分配线程

}

}

catch(Exception e)

{

e.printStackTrace()

}

}

/**

* 该线程体用来处理给定的某一个客户端的消息,循环接收客户端发送

* 的每一个字符串,并输出到控制台

* @author Jacob

*

*/

class GetClientMsgHandler implements Runnable

{

/*

* 该属性是当前线程处理的具体的客户端的Socket

* @see java.lang.Runnable#run()

*/

private Socket socket

/*

* 获取客户端的地址信息

* private String hostIP

*/

/*

* 获取客户端的昵称

*/

private String nickName

/*

* 创建构造方法

*/

public GetClientMsgHandler(Socket socket)

{

this.socket = socket

/*

* 获取远端客户的Ip地址信息

* 保存客户端的IP地址字符串

* InetAddress address = socket.getInetAddress()

* hostIP = address.getHostAddress()

*/

}

/*

* 创建内部类来获取昵称

*/

private String getNickName() throws Exception

{

try

{

//服务端的输入流读取客户端发送来的昵称输出流

InputStream iin = socket.getInputStream()

InputStreamReader isr =

new InputStreamReader(iin,"UTF-8")

BufferedReader bReader = new BufferedReader(isr)

//服务端将昵称验证结果通过自身的输出流发送给客户端

OutputStream out = socket.getOutputStream()

OutputStreamWriter iosw =

new OutputStreamWriter(out,"UTF-8")

PrintWriter ipw = new PrintWriter(iosw,true)

//读取客户端发来的昵称

String nameString = bReader.readLine()

while(true)

{

if(nameString.trim().length()==0)

{

ipw.println("FAIL")

}

if(allOut.containsKey(nameString))

{

ipw.println("FAIL")

}

else

{

ipw.println("OK")

return nameString

}

nameString = bReader.readLine()

}

}

catch(Exception e)

{

throw e

}

}

@Override

public void run()

{

PrintWriter pw = null

try

{

/*

* 通过客户端的Socket获取客户端的输出流

* 用来将消息发送给客户端

*/

OutputStream os = socket.getOutputStream()

OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8")

pw = new PrintWriter(osw,true)

/*

* 将客户昵称和其所说的话作为元素存入共享集合HashMap中

*/

nickName = getNickName()

addOut(nickName, pw)

Thread.sleep(100)

/*

* 服务端通知所有客户端,某用户登录

*/

sendMsgToAll("[系统通知]:欢迎**"+nickName+"**登陆聊天室!")

/*

* 通过客户端的Socket获取输入流

* 读取客户端发送来的信息

*/

InputStream is = socket.getInputStream()

InputStreamReader isr = new InputStreamReader(is,"UTF-8")

BufferedReader br = new BufferedReader(isr)

String msgString = null

while((msgString = br.readLine())!=null)

{

//验证是否是私聊

if(msgString.startsWith("@"))

{

/*

* 私聊格式:@昵称:内容

*/

int index = msgString.indexOf(":")

if(index >=0)

{

//获取昵称

String name = msgString.substring(1,index)

String info = msgString.substring(index+1,msgString.length())

info = nickName + "对你说:"+ info

//将私聊信息发送出去

sendMsgToPrivate(name, info)

//服务端不在广播私聊的信息

continue

}

}

/*

* 遍历所有输出流,将该客户端发送的信息转发给所有客户端

*/

System.out.println(nickName+"说:"+ msgString)

sendMsgToAll(nickName+"说:"+ msgString)

}

}

catch (Exception e)

{

/*

* 因为Win系统用户的客户端断开连接后,br.readLine()方法读取

* 不到信息就会抛出异常,而Linux系统会持续发送null;

* 因此这里就不在将捕获的异常抛出了。

*/

}

finally

{

/*

* 当执行到此处时,说明客户端已经与服务端断开连接

* 则将该客户端存在共享集合中的输出流删除

*/

removeOut(nickName)

/*

* 通知所有客户端,某某客户已经下线

*/

sendMsgToAll("[系统通知]:"+nickName + "已经下线了。")

/*

* 关闭socket,则通过Socket获取的输入输出流也一同关闭了

*/

if(socket!=null)

{

try

{

socket.close()

}

catch(IOException e)

{

e.printStackTrace()

}

}

}

}

}

public static void main(String[] args)

{

chatServer server = new chatServer()

server.start()

}

}

我的作业,供你参考

Java多人聊天可以使用Java的Socket编程实现,主要的思路是:使用服务器来维护所有客户端的连接,并将客户端之间的聊天信息进行转发。

具体的实现步骤如下:

创建服务器端:使用ServerSocket类创建一个服务器端,并监听指定的端口,等待客户端的连接。

创建客户端:使用Socket类创建一个客户端,并连接到服务器端。

实现聊天功能:客户端和服务器端之间可以通过输入和输出流进行通信,客户端将聊天信息发送给服务器,服务器再将其转发给其他客户端。

处理异常:在实现聊天功能时,需要注意处理可能出现的异常,例如连接异常、输入输出异常等等。

一个简单的Java多人聊天程序的代码框架如下:

服务器端:

import java.io.IOException

import java.net.ServerSocket

import java.net.Socket

import java.util.ArrayList

public class ChatServer {

private ServerSocket serverSocket

private ArrayList<ClientHandler>clients

public ChatServer(int port) throws IOException {

serverSocket = new ServerSocket(port)

clients = new ArrayList<ClientHandler>()

System.out.println("服务器已启动,等待客户端连接...")

}

public void start() throws IOException {

while (true) {

Socket socket = serverSocket.accept()

ClientHandler client = new ClientHandler(socket, this)

clients.add(client)

client.start()

}

}

public void broadcast(String message) {

for (ClientHandler client : clients) {

client.sendMessage(message)

}

}

public void removeClient(ClientHandler client) {

clients.remove(client)

}

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

ChatServer server = new ChatServer(12345)

server.start()

}

}

客户端:

import java.io.BufferedReader

import java.io.IOException

import java.io.InputStreamReader

import java.io.PrintWriter

import java.net.Socket

public class ChatClient {

private Socket socket

private BufferedReader reader

private PrintWriter writer

private String name

public ChatClient(String serverAddress, int port, String name) throws IOException {

socket = new Socket(serverAddress, port)

reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))

writer = new PrintWriter(socket.getOutputStream(), true)

this.name = name

}

public void start() throws IOException {

System.out.println("欢迎来到聊天室!")

new Thread(new IncomingMessageHandler()).start()

new Thread(new OutgoingMessageHandler()).start()

}

private class IncomingMessageHandler implements Runnable {

@Override

public void run() {

try {

while (true) {

String message = reader.readLine()

if (message == null) {

break

}

System.out.println(message)

}

} catch (IOException e) {

e.printStackTrace()

} finally {

close()

}

}

}

private class OutgoingMessageHandler implements Runnable {

@Override

public void run() {

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))

try {

while (true) {

String message = reader.readLine()

if (message.equals("quit")) {

break

}

writer.println(name + ": " + message)

}

} catch (IOException e) {

e.printStackTrace;

} finally {

close()

}

}

}


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/11126665.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-13
下一篇2023-05-13

发表评论

登录后才能评论

评论列表(0条)

    保存