使用Socket进行简单的聊天功能
//在初步了解了Socket的基础知识,了解了Socket的传输之后,简单写一个手机用Socket进行聊天功能的Demo
//1.先写一个server和client进行交互的例子
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9999);
//设置超时时间,过了时间会自动关闭
server.setSoTimeout(60000*10);
Socket client = server.accept();
while(true){
//输入流
DataInputStream dis = new DataInputStream(client.getInputStream());
String a = dis.readUTF();
//输出流
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("服务器--》:"+a);
dos.flush();
}
}
}
public class Client {
public static void main(String[] args) throws IOException {
Socket client = new Socket("localhost", 9999);
//从键盘写入
BufferedReader b = new BufferedReader(new InputStreamReader(System.in));
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
DataInputStream dis = new DataInputStream(client.getInputStream());
while(true){
String re = b.readLine();
//输出流
dos.writeUTF(re);
dos.flush();
//输入流
String a = dis.readUTF();
System.out.println(a);
}
}
}
server端使用死循环使客户端可以一直访问。
先运行server再运行client,在控制台输入:
得到这样表示已经成功进行了,server和client的通信。(使用流的方式)
为了是程序更加适用,改进client端进行代码分离和多线程使用:
public class Client {
public static void main(String[] args) throws IOException {
Socket client = new Socket("localhost", 9999);
//启动发送线程
new Thread(new Send(client)).start();
//启动接收线程
new Thread(new Receive(client)).start();
}
}
提出client的发送和接收方法成单独的目的是让输入和输出流相互独立
使用BufferReader用来进行从键盘的写入(注:BufferReader的readLine()方法是阻塞的,进行到readLine()方法会阻塞,直到接收到键盘输入的数据才会继续运行):
发送类:
/*
注(Client的Send类思路介绍):
新建一个发送的线程。
把Client类中的BufferReader和DataOutputStream提出到send类中
在无惨构造进行BufferReader的初始化
使用有参构造调用BufferReader并初始化DataOutputStream
在线程的run方法里面进行判断,构造输出流是否成功,是成功进行发送操作(提出来的send())
发送操作(send())分两步:
1.从键盘接收数据(getMesforConsole()方法)
2.发送数据
*/
public class Send implements Runnable{
//键盘写入流
private BufferedReader buff;
//管道输出流
private DataOutputStream dos;
//控制线程的标识
private boolean isRunning = true;
//构造器里初始化
public Send() {
buff = new BufferedReader(new InputStreamReader(System.in));
}
//把管道传进来
public Send(Socket client){
this();
try {
dos = new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
// e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dos,buff);
}
}
//从控制台接收数据
public String getMesforConsole(){
try {
return buff.readLine();
} catch (IOException e) {
// e.printStackTrace();
}
return "";
}
//发送信息
public void send(){
String console = getMesforConsole();
try {
if(null != console && console != ""){
dos.writeUTF(console);
dos.flush();
}
} catch (IOException e) {
// e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dos,buff);
}
}
@Override
public void run() {
while(isRunning){
send();
}
}
接收类:
/*
注(Client的Send类思路介绍):
构建接受类run()里面调用接收方法,输出接收到的数据
*/
public class Receive implements Runnable{
private DataInputStream dis;
private boolean isRunning = true;
public Receive() {
}
public Receive(Socket client){
this();
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
// e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dis);
}
}
/**
* 接收数据
* @return
*/
public String receive(){
String receive = "";
try {
receive = dis.readUTF();
} catch (IOException e) {
// e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dis);
}
return receive;
}
@Override
public void run() {
while(isRunning){
System.out.println(receive());
}
}
}
关闭流的工具类CloseUtil:
/**
* 关闭所有流,工具类,可关闭多个(传进来多个以“,”分割)
* @author Administrator
*
*/
public class CloseUtil {
public static void closeAll(Closeable... io){
for(Closeable temp:io){
try {
if(null != temp){
temp.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这个时候只能单个client进行访问,改进server端(支持多客户端访问):
/*
注(Server思路介绍):
构建private List<MyChannel> list = new ArrayList<MyChannel>();
进行存储所有的管道,也就是客户端的连接每个连接可以称为一个管道
使用list统一管理容器
提出send和reciive方法还有发送给他人的sendOthers(发送给所有人)
*/
public class Server {
private List<MyChannel> list = new ArrayList<MyChannel>();
public static void main(String[] args) throws IOException {
//设置超时时间,过了时间会自动关闭
// server.setSoTimeout(60000*10);
new Server().start();
}
public void start() throws IOException{
ServerSocket server = new ServerSocket(9999);
ExecutorService pool = Executors.newFixedThreadPool(2);
while(true){
//每隔进来会开启一个线程
Socket client = server.accept();
MyChannel myChannel = new MyChannel(client);
//将进来的管道都加进去.加入到容器统一管理
list.add(myChannel);//统一管理
// new Thread(myChannel).start();//一条道路
pool.execute(myChannel);
}
}
//写到内部类里面,便于访问私有信息
private class MyChannel implements Runnable{
private DataInputStream dis;
private DataOutputStream dos;
private boolean isRunning = true;
private String name;
//在有参构造初始化
public MyChannel(final Socket client){
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.name = dis.readUTF();
send("欢迎进入聊天室");
sendOthers(name+" 加入了聊天室",true);
} catch (IOException e) {
// e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dis,dos);
}
}
/**
* 接收方法
*/
public String receive(){
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
// e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dis);
list.remove(this);
}
return msg;
}
/**
* 发送给自己方法
*/
public void send(String msg){
if(null == msg || msg.equals("")){
return;
}
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
// e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dos);
list.remove(this);
// sendOthers(name+" 离开了聊天室");
}
}
/**
* 发送给别人的
* @param msg
*/
public void sendOthers(String msg,boolean sys){//true表示系统发的,false表示给别人发的
if(msg.startsWith("@") && msg.indexOf(":") > -1){
String name = msg.substring(1, msg.indexOf(":"));
String context = msg.substring(msg.indexOf(":") + 1);
for(MyChannel other:list){
if(other.name.equals(name)){
other.send(this.name+"私聊你说:"+context);
}
}
}else{
for(MyChannel other:list){
if(other == this){
continue;
}
if(sys){//系统提示信息
other.send("系统信息 "+msg);
}else{
other.send(this.name+"对所有人说:"+msg);
}
}
}
}
@Override
public void run() {
while(isRunning){
// send(receive());//只发自己
//发送给别人
sendOthers(receive(),false);
}
}
}
}
Tip:在实现上述功能的基础上可以进行功能增加,添加客户端用户姓名,和改进让用户进行私聊。