java socket多线程聊天室_Java学习之结合多线程技术,实现客户端和服务端任意通讯的“聊天室”功能...

1.、首先,是基于TCP的Socket的单次通信,简单的发送一次消息,得到一次消息

代码如下:

客户端

package com.chenpeng.client;

import java.io.BufferedReader;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.Socket;

import java.util.ArrayList;

import java.util.List;

public class Client {

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

//(1)创建Socket对象

Socket client=new Socket("localhost",9999);

//(2)从键盘获取数据

BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

//(3)获取输出流

DataOutputStream dos=new DataOutputStream(client.getOutputStream());

//(4)获取输入流

DataInputStream dis=new DataInputStream(client.getInputStream());

String str=br.readLine();

dos.writeUTF(str);//向服务器端发送数据

System.out.println(dis.readUTF());//接收服务器端的数据

}

}

服务器端:

package com.chenpeng.server;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.ArrayList;

import java.util.List;

public class Server {

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

System.out.println("----------------服务器端已开启---------------------");

//(1)创建ServerSocket对象

ServerSocket server=new ServerSocket(9999);

//(2)监听客户端是否有客户端请求连接

Socket socket=server.accept();

//获取输入流

DataInputStream dis=new DataInputStream(socket.getInputStream());

//获取输出流

DataOutputStream dos=new DataOutputStream(socket.getOutputStream());

String str=dis.readUTF();

System.out.println("客户端发送了:"+str);

dos.writeUTF("服务器端收到了:"+str);

//(5)关闭流

CloseUtil.closeAll(dos,dis,socket);

}

}

工具类:省略,主要是关闭流和socket

缺点:

只能实现客户端发一次消息,服务器端发一次信息

如果我们要实现像QQ一样聊天那样,可以一直发,一直回复怎么办呢?

答:使用循环就可以一直发,使用多线程,发送消息和接收消息使用线程来就可以解决不用先发之后,再等服务器端的回复,而是发送和回复都可以同时进行。

接收数据的方法:

package com.chenpeng.util;

import java.io.DataInputStream;

import java.io.IOException;

import java.net.Socket;

/**

* 接收数据的线程

* @author chenpeng

*

*/

public class Receive implements Runnable {

//用于接收数据的数据流

private DataInputStream dis;

private boolean flag= true;

//构造方法

public Receive(Socket client) {

try {

dis = new DataInputStream(client.getInputStream());

} catch (IOException e) {

//有异常,则关闭资源

flag = false;

CloseUtil.closeAll(dis,client);

}

}

//接收数据的方法

private String getMessage() {

String str ="";

try {

str = dis.readUTF();

} catch (IOException e) {

flag = false;

CloseUtil.closeAll(dis);

}

return str;

}

@Override

public void run() {

while(flag) {

System.out.println(this.getMessage());

}

}

}

发送数据的方法:

package com.chenpeng.util;

import java.io.BufferedReader;

import java.io.DataOutputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.Socket;

/**

* 发送消息的线程

* @author chenpeng

*

*/

public class Send implements Runnable{

//从键盘获取数据

private BufferedReader br;

//发送数据使用数据输出流

private DataOutputStream dos;

private boolean flag = true;//默认为true

//无参构造方法

public Send() {

br = new BufferedReader(new InputStreamReader(System.in));

}

//有参构造器

public Send(Socket client) {

this();//调用本类的无参构造方法

try {

dos = new DataOutputStream(client.getOutputStream());

} catch (IOException e) {//有问题则关闭

flag= false;

CloseUtil.closeAll(dos,client);

}

}

//从键盘得到信息

private String getMessage() {

String str="";

try {

str =br.readLine();

} catch (IOException e) {

flag = false;//

CloseUtil.closeAll(br);

}

return str;

}

//发送给服务端

private void send(String str) {

try {

dos.writeUTF(str);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

try {

dos.flush();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

@Override

public void run() {

while(flag) {

//调用发送信息的方法

this.send(getMessage());

}

}

}

服务端方法:

/**

* 服务端

*

* @author chenpeng

*

*/

public class Server {

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

System.out.println("----------服务端-----------");

// 1.创建一个ServerSocket对象

ServerSocket server = new ServerSocket(8848);

// 2.监听客户端是否发请求过来

Socket client = server.accept();

// 创建接收数据的线程

Receive receive = new Receive(client);

// 创建发送数据回去的线程

Send send = new Send(client);

//启动线程

new Thread(receive).start();

new Thread(send).start();

}

}

客户端的方法:

package com.chenpeng.Thread;

import java.io.IOException;

import java.net.Socket;

import java.net.UnknownHostException;

/**

* 客户端

* @author chenpeng

*

*/

public class Client {

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

System.out.println("--------客户端---------");

//创建Socket对象

Socket client= new Socket("localhost",8848);

//创建发送的线程类对象

Send send = new Send(client);

//创建接收的线程类对象

Receive receive = new Receive(client);

//创建Thread类对象的线程并启动线程

new Thread(send).start();

new Thread(receive).start();

}

}

这时候就可以实现客服端发消息,服务器端也能发消息,简单实现了QQ的聊天功能,原理就是两边都使用发送数据和接收数据的线程,然后一直循环。

但是有个小问题,如果我再启动一个客户端发送消息,是不会到服务端的?这里等我思考之后什么回事再解决吧,今天主要是实现“群聊”的功能

3、现在,到了实现“群聊”的时候了

思路:

1、首先基本上和上面客户端和服务器端一直聊没区别,也是使用了发送消息的线程和接收消息的线程

2、但是这里问题来了,一个客户端发送消息,如何让其他的客户端接收到消息呢?这时候,我们需要在服务器端来一个线程类MyChannel,用来记录每个客户端发送的数据,然后再使用群发的功能sendOther(),不能发给自己,所以我们在服务器端上面创建了一个Arraylist集合,用于存放运行着的线程。

package com.chenpeng.util;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.net.Socket;

import java.util.List;

/**

* 每一个客户端都是一条道路

* 1.输入流

* 2.输出流

* 3.接收数据

* 4.发送数据

* @author chenpeng

*

*/

public class MyChannel implements Runnable {

private DataInputStream dis;

private DataOutputStream dos;

private boolean flag = true;

//构造方法

public MyChannel(Socket client) {

try {

dis = new DataInputStream(client.getInputStream());

dos = new DataOutputStream(client.getOutputStream());

} catch (IOException e) {

flag = false;

CloseUtil.closeAll(dis,dos);

}

}

//接收数据的方法

private String receive() {

String str ="";

try {

str = dis.readUTF();

} catch (IOException e) {

flag = false;

CloseUtil.closeAll(dis,dos);

Server.list.remove(this);

}

return str;

}

//发送数据的方法

private void send(String str) {

if(str!=null&&str.length()!=0) {

try {

dos.writeUTF(str);

dos.flush();

} catch (IOException e) {

flag= false;

CloseUtil.closeAll(dos,dis);

Server.list.remove(this);

}

}

}

//转发的方法

private void sendOther() {

String str = this.receive();

List list = Server.list;

for(MyChannel other: list) {

if(other == this) {

continue;//不发给自己

}

other.send(str);//发送数组

}

}

@Override

public void run() {

while(flag) {

//调用发送数据的方法

sendOther();

}

}

}

注意:客户端这里使用了while循环,一直监听者客户端有没有客户端连接

总结:

单次聊天只能先发数据,然后才能接收数据,如果不发就收不到,因为接收和收到是在一个线程里面实现的,不符合实际的情况

解决方案:

1、想要多次聊天可以使用循环

2、先发后收的的问题可以使用线程来解决,一个接收数据的线程,一个发送数据的线程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值