一、概述

1.目标:在上一个版本非线程的聊天系统中,出于要不断监听接收新client和接收client发出的信息,把accept()和DataInputStream.readUTF()分别写在了while()死循环里,由于俩方法都是阻塞性方法,所以一方阻塞,另一方永远得不到执行,所以,在上述代码中,只能监听第一个client的发送信息,新的client永远连接不上。

2.思路:

(1)把socet.accept()接收线程和DataInputStream.readUTF()的处理分开,把前者交给server端的处理,server端把接收到的clent封装到线程里,把DataInputStream.readUTF()的逻辑交给线程处理。

(2)采用异步IO

二、代码

1.ChatServer.java

import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException; public class ChatServer{ private ServerSocket ss = null;
private boolean started = false; public static void main(String[] args) {
new ChatServer().start();
} public void start(){ try {
ss = new ServerSocket(8888);
started = true;
} catch (IOException e1) {
e1.printStackTrace();
} try {
while(started){
//!!!!!注意accept()是阻塞性方法,当被readUTF()阻塞时它不会被执行
Socket s = ss.accept();
System.out.println("a client connect------"+s);
new Thread(new Client(s)).start();
}
} catch (EOFException e) {
System.out.println("客户端已关闭!");
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//if(dis != null ) dis.close();
//if(s != null ) s.close();
if(ss != null ) ss.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} private class Client implements Runnable{ private Socket s;
private DataInputStream dis = null;
boolean bConnect = false; public Client(Socket s) {
super();
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
bConnect = true;
} catch (IOException e) {
e.printStackTrace();
}
} @Override
public void run() {
try {
while(bConnect){
//!!!!!注意readUTF()是阻塞性方法
System.out.println(dis.readUTF());
}
} catch (EOFException e) {
System.out.println("客户端已关闭!");
}
catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(dis != null ) dis.close();
if(s != null ) s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
} }

  

2.ChatClient.java

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException; public class ChatClient extends Frame{ private TextField tfText;
private TextArea taContent;
// private Button btnSend;
private Socket socket;
private DataOutputStream dos; public void launchFrame(){ addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
disconnect();
System.exit(0);
}
}); tfText = new TextField();
taContent = new TextArea();
// btnSend = new Button("发送");
//
// btnSend.addActionListener(new ActionListener() {
// @Override
// public void actionPerformed(ActionEvent e) {
// //taContent.setText(taContent.getText()+"\n\r"+tfText.getText());
// //tfText.setText("");
// try {
// DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
// dos.writeUTF(tfText.getText());
// dos.close();
// socket.close();
// } catch (IOException e1) {
// e1.printStackTrace();
// }
// }
// }); tfText.addActionListener(new TFListener()); add(taContent,BorderLayout.NORTH);
add(tfText,BorderLayout.CENTER);
// add(btnSend,BorderLayout.SOUTH);
setLocation(300, 100);
pack();
setVisible(true);
connect("localhost", 8888);
} //当调用了此方法,会自动把成员变量给socket连接上server
public void connect(String address, int port){
try {
socket = new Socket(address, port);
dos = new DataOutputStream(socket.getOutputStream());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} //退出时释放资源
public void disconnect(){
try {
if(dos != null ) dos.close();
if(socket != null ) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
} class TFListener implements ActionListener{ @Override
public void actionPerformed(ActionEvent e) {
taContent.setText(taContent.getText().trim()+"\n\r"+tfText.getText()); try {
dos.writeUTF(tfText.getText());
dos.flush();
tfText.setText("");
//dos.close();
//socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} public static void main(String[] args) {
new ChatClient().launchFrame();
}
}

  

三、运行结果

Java-在线聊天系统-线程的更多相关文章

  1. java动态编译 (java在线执行代码后端实现原理)(二)

    在上一篇java动态编译 (java在线执行代码后端实现原理(一))文章中实现了 字符串编译成字节码,然后通过反射来运行代码的demo.这一篇文章提供一个如何防止死循环的代码占用cpu的问题. 思路: ...

  2. 小谈Java里的线程

    今天,我们来谈一谈Java里的线程. 一.进程与线程的基本概念 大家可能没听过线程这个概念,但是相信,用计算机的朋友都听过进程这个概念.打开电脑的任务管理器,我们就可以看到许多进程.它们主要分为三类, ...

  3. Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...

  4. 【转】关于Java的Daemon线程的理解

    原文地址:http://www.cnblogs.com/ChrisWang/archive/2009/11/28/1612815.html 关于Java的Daemon线程的理解 网上对Java的Dae ...

  5. Java四种线程池

    Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...

  6. 我是怎么开发一个小型java在线学习网站的

    2016/1/27 11:55:14 我是怎么开发一个小型java在线学习网站的 一直想做一个自己的网站(非博客),但是又不知道做什么内容的好,又一次看到了w3schools,就萌发了开发一个在线ja ...

  7. java笔记--使用线程池优化多线程编程

    使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库 ...

  8. Java中的线程

    http://hi.baidu.com/ochzqvztdbabcir/item/ab9758f9cfab6a5ac9f337d4 相濡以沫 Java语法总结 - 线程 一 提到线程好像是件很麻烦很复 ...

  9. [译]线程生命周期-理解Java中的线程状态

    线程生命周期-理解Java中的线程状态 在多线程编程环境下,理解线程生命周期和线程状态非常重要. 在上一篇教程中,我们已经学习了如何创建java线程:实现Runnable接口或者成为Thread的子类 ...

  10. Java多线程和线程池

    转自:http://blog.csdn.net/u013142781/article/details/51387749 1.为什么要使用线程池 在Java中,如果每个请求到达就创建一个新线程,开销是相 ...

随机推荐

  1. 64.OV7725初始化配置

    所有的结局都是好的,不好,是因为你还有坚持到最好. OV7725摄像头的初始化配置,需要SCCB总线即IIC接口配置.先发送配置数据到OV7725中,然后通过随机读取对应地址的数据来验证数据是否写进去 ...

  2. QT 按钮(4种样式)

    // 1.正常  btnNormal_ = new QPushButton("Normal Button", this);  // 2.可停驻  btnCheck_ = new Q ...

  3. linux 简单的DMA例程

    一个简单的使用DMA 例子 示例:下面是一个简单的使用DMA进行传输的驱动程序,它是一个假想的设备,只列出DMA相关的部分来说明驱动程序中如何使用DMA的. 函数dad_transfer是设置DMA对 ...

  4. TList,TObjectList 使用——资源释放

    TOjectList = Class (Tlist); TOjectList继承Tlist,从名字上看就可以知道它是专门为对象列表制作的,那么他到底丰富了那些功能呢? 首先是 TObject 作为对象 ...

  5. Ionic 2 Guide

    Ionic 2 Guide 最近一直没更新博客,业余时间都在翻译Ionic2的文档.之前本来是想写一个入门,后来觉得干脆把官方文档翻译一下算了,因为官方文档就是最好的入门教程.后来越翻译越觉得这个事情 ...

  6. Android -- 分享功能和打开指定程序

    打开指定程序                                                                                Intent intent ...

  7. Github的使用以及Git的简单入门 - 课程作业三

    GitHub创建项目 登录GitHub,在个人主页创建项目(repository) 创建后会生成2个文件,README.md和.gitignore.如图 创建本地仓库 如果是第一次使用git的话,需要 ...

  8. 关于ASCII、GB231、GBK、UTF-8/UTF8、ANSI、unicode的学习笔记

    继续上次的学习内容,写一些自己学习的笔记吧!总是觉得没有笔记的学习总是不那么踏实,我承认自己是个记忆力很差的人,特别羡慕那些可以把自己学过的东西记得很牢靠的人.哎!可惜我不是,那只能做出来点东西,就算 ...

  9. ELK kibana查询与过滤(17th)

    在kibana中,可通过搜索查询过滤事务或者在visualization界面点击元素过滤. 创建查询 在Discover界面的搜索栏输入要查询的字段.查询语法是基于Lucene的查询语法.允许布尔运算 ...

  10. C#制作高仿360安全卫士窗体(四)- 水晶按钮

    项目越来越紧,我也乐此不疲.自从上次C#制作高仿360安全卫士窗体(三)出来之后,就开始有一些人在说为什么还在坚持写这么落后的东西.我想说的是,我是从事企业信息化工作的,所有程序都只对内部使用.所以只 ...