2013年3月月 发布的文章

jenkins远程部署Deploy Plugin配置

jenkins远程部署配置
1、安装Deploy Plugin插件
This plugin takes a war/ear file and deploys that to a running remote application server at the end of a build. The implementation is based on Cargo. The list of currently supported containers include:
Tomcat 4.x/5.x/6.x/7.x
JBoss 3.x/4.x
Glassfish 2.x/3.x
远程部署不包括resin啊!
插件下载地址:http://ftp-nyc.osuosl.org/pub/jenkins/plugins/deploy/1.9/deploy.hpi
版本:1.9
插件地址:https://wiki.jenkins-ci.org/display/JENKINS/Deploy+Plugin
登录Jenkins
选择 Jenkins->系统管理->管理插件->高级 上传插件,选择deploy.hpi文件上传,上传完成后需要重新启动Jenkins
在插件管理的已安装标签下,可以看到“Deploy to container Plugin”,证明插件安装成功
2、配置job
build已经完成打war包功能,Deploy Plugin只是负责远程部署
在“构建后操作” 选择“deploy war/era to a container”
输入下面参数:
WAR/EAR files:war包路径
Context path:空白,未填,还没弄清楚用处
Container:下拉菜单,选择容器
Deploy on failure:未勾选,还没弄清楚用处
可参考下图:
远程部署配置
部署结果:
远程部署成功
部署碰到个问题:
Jenkins控制台没有报任何错误,build也是成功的,但是就是未在远端服务器看到war包,查了资料,原来war包路径错误会导致部署失败,但是不会报错(猜想:因为此时已经build完成了,不属于build的范围,所以不会改变build结果)。
解决问题参考这个网址: http://stackoverflow.com/questions/9277223/jenkins-auto-deploy-tomcat-7
I was having the same problem, and in my case the (relative) path to the WAR file was incorrect. Apparently if you don’t have it exactly correct (it needs to be relative to the workspace root) then the deploy plugin will silently fail. In my case the path was:
target/whatever.war
Once that was fixed, I ran into a different problem in that the plugin expects to connect to the manager/text version of Tomcat Manager, rather than the manager/html version that I usually configure by default. You’ll need a line in your tomcat-users.xml file like the following:
(This is in addition to the “manager-gui” role you probably already have set up.)
Once those changes were made, the build and deployment worked just fine.

环境准备-Hadoop安装过程

环境准备
1、利用vmware安装centOS6系统
2、Window开启VMware Network Adapter VMnet8
注意:虚拟机网络类型选择NAT;安装操作系统是开启网络默认启动;获取IP为DNS自动获取;虚拟机安装成功后,关闭防火墙;检查虚拟机网络和物理机网络是否能ping通;



虚拟机ping物理主机
3、安装jdk,版本大为1.6,采用sun公司的jdk而不是openjdk
参考:《Linux下安装jdk

爆笑体育2013年第一期20130318

爆笑体育2013年第一期20130318,下班看看这个还真不错,哈哈O(∩_∩)O哈哈哈~

IT人必看颈椎保健操

IT人必看颈椎保健操–长时间一个姿势看书、玩电脑等易引发颈椎病,且男性的发病率高于女性。关注自己的健康,阻止亚健康,毕竟身体是革命的本钱嘛

关注自己的健康–阻止亚健康

cookie是什么

Cookie(复数形态Cookies),中文名称为小型文本文件或小甜饼,指某些网站为了辨别用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。定义于RFC2109。为网景公司的前雇员Lou Montulli在1993年3月所发明。
Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie。
内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了,其存在时间是短暂的。硬盘Cookie保存在硬盘里,有一个过期时间,除非用户手工清理或到了过期时间,硬盘Cookie不会被删除,其存在时间是长期的。所以,按存在时间,可分为非持久Cookie和持久Cookie。
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两瓶饮料。最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么。 所以Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。
在刚才的购物场景中,当用户选购了第一项商品,服务器在向用户发送网页的同时,还发送了一段Cookie,记录着那项商品的信息。当用户访问另一个页面,浏览器会把Cookie发送给服务器,于是服务器知道他之前选购了什么。用户继续选购饮料,服务器就在原来那段Cookie里追加新的商品信息。结帐时,服务器读取发送来的Cookie就行了。
Cookie另一个典型的应用是当登录一个网站时,网站往往会请求用户输入用户名和密码,并且用户可以勾选“下次自动登录”。如果勾选了,那么下次访问同一网站时,用户会发现没输入用户名和密码就已经登录了。这正是因为前一次登录时,服务器发送了包含登录凭据(用户名加密码的某种加密形式)的Cookie到用户的硬盘上。第二次登录时,(如果该Cookie尚未到期)浏览器会发送该Cookie,服务器验证凭据,于是不必输入用户名和密码就让用户登录了。
cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
由于在HTTP请求中的cookie是明文传递的,所以安全性成问题。(除非用HTTPS)
Cookie的大小限制在4KB左右。对于复杂的存储需求来说是不够用的。
虽然cookies没有中电脑病毒那么危险,但它仍包含了一些敏感信息:用户名,电脑名,使用的浏览器和曾经访问的网站。用户不希望这些内容泄漏出去,尤其是当其中还包含有私人信息的时候。
这并非危言耸听,跨站点脚本(Cross site scripting)可以达到此目的。在受到跨站点脚本攻击时,cookie盗贼和cookie毒药将窃取内容。一旦cookie落入攻击者手中,它将会重现其价值。
Cookie盗贼:搜集用户cookie并发给攻击者的黑客。攻击者将利用cookie信息通过合法手段进入用户帐户。
Cookie投毒:一般认为,Cookie在储存和传回服务器期间没有被修改过,而攻击者会在cookie送回服务器之前对其进行修改,达到自己的目的。例如,在一个购物网站的cookie中包含了顾客应付的款项,攻击者将该值改小,达到少付款的目的。这就是cookie投毒。
网络信标(web beacon),又称网页臭虫(web bug),是可以暗藏在任何网页元素或邮件内的1像素大小的透明GIF或PNG图片,常用来收集目标电脑用户的上网习惯等数据,并将这些数据写入Cookie。网络信标在邮件跟踪和垃圾邮件中较为常用。

产品-项目组如何进行有效的沟通

  我们每天都在通过各种方式与人沟通,但是这些沟通是真正有效的吗?我们是否总是在不知不觉中,被沟通障碍牵绊住了前进的脚步,沉浸在消极的工作情绪之中却还不自知呢?
以下是我在工作中总结的一些沟通心得,在此与大家分享。
项目中常见的沟通方式:
通过文档沟通:
优点:不受文字数量的限制,内容具体;便于查阅存档及日后的统一管理;适合描述功能多、业务复杂的 项目;适合跨部门协作的项目;
缺点:不容易建立统一标准;面向不同角色,阅读时不容易找到重点;费时;理解成本高,沟通效率低
通过邮件沟通:
优点:打破时间和空间的限制;便于查阅记录;方便为多人发送附件;比较正式,适合报告工作进度或通 报项目状况等
缺点:正文不适宜太长;传递信息不即时(有时容易被忽略或丢失);不清楚语言环境有时容易误读;不 利于处理争议或敏感问题
通过IM沟通:
优点:沟通方便;容易消除紧张情绪;截图、发送文件方便;可多人对话;适合相熟的同事之间沟通,畅 所欲言;适合解决争议不大的问题,
缺点:容易被忽略;一些复杂的问题很难描述清楚;容易误解;查询记录时不是很方便(里面可能夹杂了 不少无关内容);不利于解决争议;过于随意,不适合说重要且紧急的问题
通过电话沟通:
优点:即时、有效,沟通效率较高;适合解决紧急但不太重要的问题
缺点:不利于传达微妙的情感;特别复杂的问题仍不容易说清楚,有可能引起误会;不方便查看图片等 (可配合IM使用);不便查找记录
面对面沟通:
优点:真实、拉近距离(很多误会可由此解开);便于说明复杂问题;沟通效率高
缺点:无记录;沟通成本略高;多人沟通时效率可能较低;一旦陷入僵局回旋余地较小(面对面沟通时心 态一定要平和,以解决问题为目的)
会议沟通:
优点:集思广益、开拓思路,更多角度了解他人的观点;适用于跨部门、协同解决问题、头脑风暴等
缺点:若方法不得当会导致效率极低(如果需要在会上做出决定,最好先提前一对一沟通,有备而来)
特点比较(世事无绝对,仅供参考):
适用环境(世事无绝对,仅供参考):
了解时间管理的人都知道,要首先关注重要但不紧急的事情;其次还要尽量处理好既重要又紧急的事情。因此在沟通中,要充分的重视文档、多人会议、面对面沟通、邮件等沟通方式。对于一个基层员工,要特别注意掌握面对面沟通和邮件沟通的方法。
总结:每种沟通方式都有各自的特点,很难彻底舍弃其中的任何一种,在工作中应该根据情况选择适合的沟通方式。
比如说:在项目开始之前,可以通过多人会议进行头脑风暴征求大家的意见;策划中撰写需求文档;文档写完后先面对面给其他项目人员讲述一遍思路,之后再配合IM、电话等方式即时解决后续问题;制作原型的过程中可以随时请大家在IM上提意见;通过邮件定期监控项目进度和问题;发现项目成员有负面情绪了,赶紧面对面沟通一下,……
项目中常见的沟通问题:
中国人的性格比较含蓄,又怕伤和气。既不善于主动提问,也不善于表达内心想法。于是项目中常遇到这样的情况:
1. 用文档代替正常沟通。
很少有PM发完文档,会快速跑过来给你讲一遍他的思路。这也是人之常情,人家都没问,干嘛自己要跑过去说呢。不过有文档的好处就是,别人看到不懂的地方会去问。
2. 用正常沟通代替文档
对于大型或复杂的项目,需要文档来解释说明,这是其他任何一种沟通方式也无法取代的。文档的缺失,不利于大家正确理解项目,也不利于发现问题。这样出来的结果很难令人满意。
3. 视觉或前端没看懂交互稿要表达的意思,或者是感到存在问题,却没有提出来。
作为交互设计师,我会尽量把交互稿做的精致些,配上详细的说明,但最后的结果总是不如预期理想。
4. 还有很多大家当初没发现的问题,制作完却成了大问题……
……
面对这些问题,我也在不断的思考解决方法,目前想到的如下:
1. 作为PM,还是要尽量写需求文档。首先,需求文档对理清PM的思路非常有帮助,可以通过它发现自己还有哪些地方没考虑周全;另一方面,它是设计的重要参考依据,靠简单的沟通不可能遍历到所有的用例和需求点;第三,文档可以帮助其他项目成员有针对性的提出问题,而不是感到困惑和无所适从。
也许有人会问,如果交互设计师从一开始就参与到项目中,甚至是参与需求的确定,还要写需求文档吗?答案是肯定的。需求文档可以规范的把需求要点有序的整理起来,对后续提高项目效率非常有帮助。
2. PM不写需求文档怎么办?
将心比心,没有人不热爱自己负责的产品。PM不写需求文档,一定有自己的立场和原因。作为项目组成员,可以总结自己在项目中需要知道和了解的问题,列出一份清单,请PM回答。相信每个PM都不会拒绝为大家回答问题吧。如果觉得对方回答的不清楚,可以继续细化问题,直至回答清楚为止。
3. 需求文档到底要写什么内容,是一个难题。到底什么样的需求文档能合理的概括重点内容,让后续工作顺利进行呢?我觉得这是一个长期的摸索过程,需要PM和交互、开发等角色一起讨论,通过长期的项目实践逐渐得到最适合当前团队、项目状况的文档格式。
前提是:PM及每一个项目成员要认识到大家是一个共同协作、平等互助的团队,而不是领导和被领导的关系。
4. PM不仅提供需求文档,还应向团队主要成员整体讲述一遍思路。前期沟通主要传递想法;中期沟通解决不断发现的问题,迭代需求;后期沟通确认问题是否得到解决。
5. 其他角色以此类推。
类似的项目如果做的多了,在这个过程中,就会逐渐形成规范机制,使得后面的工作越来越轻松。
通过沟通把握微妙的情感:
沟通过程不总是理性的,也有很多感性成分。
大家在一起工作,但又属于不同的部门或小组,时间长了,难免会产生各种小摩擦。正确的沟通,可以尽量避免这些不快,帮助我们更好的工作,或者说更快乐的工作。要想达到好的沟通效果,需要注意以下几点:
1. 放平心态。
不太计较得失,客观的看待问题,保持心情愉快……这些看起来谁都懂,做起来却困难的很,需要不停的在工作中磨练自己的心性。
2. 换位思考
你有没有对某人很不爽的时候?巧合的是,这个人十有八九对你也抱有同样的想法。对待同一个事情,每个人的立场不同,太过坚持自己的想法,就容易造成误解和矛盾。很难说谁对谁错,重要的是客观认识不同的立场,最后寻求一个好的解决方法。意气用事不会带来任何益处。
当你埋怨PM做的不好,沟通不到位的时候,有没有想想自己是不是也在犯同样的错误?自己有没有认真的把设计意图传达给视觉?每个人都有自己的难处,宽容、谅解,做好自己的事,也帮助别人做好他的事情,才能促使更好的结果。
3. 积极主动
多思考、多提问、多表达自己的意见。遇到不快的事情不要急着下结论,或是越想越歪,而是探清事情因果。其实,事情永远不像我们想的那么乐观,也不像我们想的那么悲观。
4. 真正认识沟通的意义
沟通是平等的,而不是一方强势的压过另一方。这是一个协作的时代,不是个人英雄主义的时代。
结语:沟通不是说学就能学的技能,而是通过后天不断的去领悟,去应用。我对沟通的认识还很粗浅,需要继续体会沟通之道。祝愿大家都能更有效的沟通,快乐的工作。

加班-产品成功

最近产品行业流传“唯快不破”四字诀,这话我是信的,只有实际运行的数据才能提供最可靠的指引。所以数据来得越快,方向就走得越准。敏捷发布,小步快走这些道理都是互联网产品项目的真理。问题是,单单从一个“快”字延伸出去,很容易唱一曲“爱拼才会赢”,6X12,甚至6X14之说大有市场。
加班并不可怕,至少我自己不怕加班,而且是习惯性每天多做几小时。过去五年的历史记录有两个,一是连续半年以上每周工作70+小时,另一个是带队报道车展的时候,连续四五天,每天工作16+小时。但正因为我加班很有经验,才对6X14持以异见。更直接点说,我不赞同把加班作为整个团队的一条口号,一种工作常态。
第一,产品项目以设计与开发为主,加班若要加出效率来,行政命令是没有用的,只能靠“质朴的热情”,即对项目有着发自内心的认同、喜爱与紧迫感,然而拥有这种热情的人终归是少数。让多数人陪着少数狂热者加班,会造成多数人的效率下降,状态疲惫。我们常见自上而下的以己推人……因为自己着急,就觉得所有人都该着急;因为自己全身心投入,就觉得所有人都不怕牺牲;可惜多数人的加班工时与进度收益之间并不成正比。
第二,有很大一部分工作,并不能靠加班来显著地推进,比如写出优质的代码,设计细腻的交互与精巧的算法,策划有创意的活动等等。简单来说,凡是脑力活动剧烈的工作,都要求在良好的精神状态下进行,才能保证质量。这就牵涉到第一条——部分人由于被迫加班,状态疲惫,不仅工作效率下降,质量也滑坡得厉害。可能是赶了两周工期,最后反而给项目带来更多的损失。
第三,产品项目中的每个工种都有农忙、农闲的时候。比如策划阶段,工程师的事儿就不多;到了开发阶段,设计师又得以清闲一段时间。调研和设计完成之后,我会建议PM休几天假放松一下,接下来,测试和发布阶段就得拼拼老命。因此将6X12设置为一种工作常态,这未免也太变态。我不止一次听见(其他公司)有人抱怨说,自己的活儿早就干完了,但下班后不准走,得留下来随时待命。这又是何苦……
前些日子,有件关于加班的事情让我印象深刻。我这边有一位资深工程师调换了部门,在新部门里升任主管职务,然后常常加班,累得够呛。之前不论我怎么激励整个团队,也很少见到他加班,现在却是主动大量地加班,任劳任怨。看来我以前对他“不愿意加班”的看法完全是个误解。这个例子说明质朴的热情往往来源于自我驱动,自己给自己设定目标,安排任务,“这是我的事情”。如果你的目标和任务来自上级,则驱动力大打折扣,“那是你的事情,我去帮你完成”。
可惜,强有力的自我驱动是件极为罕见的事情。有些人觉得自己不在其位不谋其政,有些人过于看重阶段性的物质奖励,有些人特别在乎自己的私人空间,有些人对项目的认同感始终提不起来,还有些人干脆对这份职业的投入度都不高。我在类似问题上困扰多年,积攒了几条经验:首先精简团队,至少是精简核心小组,参与(主持)项目的人越少,则归属感越强,不容易互相推卸责任。其次,核心小组要得到足够的授权,让他们感觉在做“我的事情”而不是“你的事情”,至少保证核心小组的自我驱动力。最后,我这个主管也要以身作则地加班,起到表率作用。
换个角度来看,加班对于产品成功真有那么重要吗?
最近恰好见一位豆瓣的朋友,问,你们加班多吗?答,一到19点办公室基本上就空了,早上通常是10点后才来上班……
这当然不能阻止豆瓣是一款好产品。
我在微博上说过一句话:2周一次小迭代,和3周一次迭代没有本质区别。产品胜出不会因为你每个版本的迭代都比别人快一周,而是你的判断更准确,设计更有效,实现更细腻。过度强调快快快,不仅令员工疲乏,也容易做不好产品减法。因为你能加班嘛,反而有更多工时去做次要需求。
我们都知道,产品并不是功能越多越强就越有竞争力。你拼命加班,飞快迭代,发布各式各样的型号版本,把自己搞得鸡血沸腾,但最终决定胜负的并不是速度,而是精度。拿我很钦佩的Instagram举例子,至今不开发Android版也不去完善网页端,平均一两个月才更新一个版本,不到一年用户数已经突破了700万大关!故而产品的理念与方向,比速度与激情更重要得多——但我看国内很多团队就知道抄,东抄西抄,自己的想法很少,就算有想法也往往是“抄这家”“抄那家”的贪多求全。这样的6X12,6X14又有何意义呢?真没见过几款产品单单靠“抄得快”“抄得全”就能成气候。勤不仅不能补拙,还有可能造成设计过度与产品失衡,结果越补越拙……
所以把6X12作为一句招聘提示,提前警示“有时候会非常忙哦”,这是没有问题的。但如果真心诚意这么执行下去,员工天天个个加班,时时刻刻在线,则一声叹息。从团队管理的角度上来讲,对市场与用户群的研究,尤其产品理念神马的过于飘渺,依赖超强的个人产品素质,无法用管理手段来衡量与促进,只好不得已而求其次,采用“加班”这种简单可量化的竞争手段。此法有效但不可滥用。

易成云门户开发实践总结

易成云门户开发实践总结
1、发现人员在协作开发中,存在等待现象,比如:前端开发需要model中的字段在页面中获取相应的值,但是后端开发没有做;门户后端的开发人员都应该思考:Action,Service,Model哪个该先做,这样才能协助前端展开工作。
开发的时候应该是:先接口,先model,后实现,即先model,先Action,后mock,最后Service实现;
提交任何一点代码都需要保证当前的工程是可运行的。
系统设计:
1)系统分析
2)数据模型
Done的标准
1)接口设计,输出伪代码
2)输出数据模型pdm
工作流程:
1)前后台开发人员一起根据原型,对“Action,jsp”进行分析,输出伪代码设计
2)后台开发人员对自己的业务层(Service)进行设计划分模块,列出服务清单
3)前后台分别开发,后台开发人员遵循TDD模式进行开发
发现的问题:
1.后台管理缺少原型,因此无法分辨请求是Ajax还是同步请求,返回页面还是提示也需要明确
对需求的把握的“度”的不统一:造成原因有:例如门户后端管理无原型,造成需求不明确,因此大家出现偏差,对需要的理解和执行上存在把握的“度”的不一致的问题。
解决方法:讨论时带上原型,后台管理部分的页面也需要原型设计—需要明确由谁来设计
2.前后端分离,组成两个team,而不是端到端的开发,这对大家的编程习惯是个挑战,需要我们互相学习、磨合、协作。
一、发起讨论的人必须准备的文档:
1.数据模型pdm 及相应的model代码
2.前后台接口设计的Action的伪代码
3.service与action的接口设计的伪代码
4.在wiki上发布,并邮件提醒相关参与人
二、参与讨论系统分析的人需要做的准备:
1.明确要讨论的模块的需求
2.熟悉wiki上的设计文档
2.根据文档提前列出需要明确的问题和自己的想法
三、注意事项
1.在讨论需求的时候还是需要先画出UE,这个必须有!!!!
2.需要修改则直接更新 伪代码和数据文档pdm,不必事后修改。
3.采取三三小组制,系统设计的组织形式:前台开发一人+后台开发二人 为一个小组 组与组之间的信息共享通过wiki和讨论会
4.层次较高的系统设计,仍由专人来设计、出文档,然后大家再一起讨论
5.前后端接口讨论时间:10:28-11:03 ,以后需要注意讨论的时间
四、接口设计负责人需要把你写的Action,Service,Model等类配置到spring和struts2的配置文件中 完成的标准:伪代码在框架中是可访问的,不会因为缺少配置文件而造成出现404等错误。

程序员VS编码员

程序员是脑力劳动者,编码员是体力劳动者。程序员是建筑师,编码员是泥瓦工。程序员有自主创新能力,编码员是照本宣科。
英文中 Coder 和 Programmer 都含有软件开发人员的意思,Programmer 是程序员,这个很好理解,但 Coder——这里暂且翻译成编码员——在国外有着跟程序员很大的不同。比如,在一家日本公司里,日本的软件工程师负责设计软件,编写详细说明书,制作完整的伪代码清单。他们这些人可称作是程序员。当他们完成这些事情后,设计说明书、详细说明书、伪代码等必备资料都交给中国那些外包的年轻小伙们,这些小伙阅读说明书,严格按照伪代码,把它们变换成特定语言真正可运行的代码,做这些工作的小伙只能称作编码员。可以看出,前者是用脑子的,而后者更像是一种人肉转码器。

Java编写的类QQ聊天系统

这是我09年写的第一个Java程序,一个类QQ聊天软件,当时是为了练习,现在把源码拿出来,当时水平有限,现在也没动力去优化了,希望能对Java初学者有一些帮助!说老实话,好多东西都忘了,但是仍有好多人找我,说要“交作业”和“毕业设计写论文”的,请大家先学好java基础,课本上的基础知识是有的,但是课本上却没告诉我们怎么把这些知识组合起来成为一个软件,这才是做作业、写论文的最大的价值所在,这才也是中国教育目前最大的悲哀。
实现思路:
首先每登录一个用户都会先到主持人这边登记,会记录每个用户的联系方式(这些都是只有主持人知道),然后主持人会通知已经登记的用户又有新用户加入;
李四想和张三说话,就把想说的话告诉主持人,由主持人帮你传话给张三,李四是不知道怎么才能联系到张三,但是主持人有每个用户的联系方式;
私聊是一对一聊,如果群里只有一个两个人,那这个群聊也就是私聊;
一共包含六个类:
Client.java
Clientframe.java
ClientUtil.java
Message.java
Server.java
Usermessage.java
Client.java:

import java.awt.Color;
import java.awt.FileDialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
/**
 *
 * @author tian QQ客户端
 */
public class Client extends Clientframe {
	Message receivemsg; // 在线用户之间的聊天信息
	Usermessage usermsg; // 用户信息
	Socket s;
	ObjectOutputStream oos;
	String ipaddress; // 服务器IP
	ArrayList al = new ArrayList();
	String cmd;
	boolean flag = false;
	/**
	 * 构造方法
	 */
	public Client() {
		usermsg = new Usermessage();
		try {
			usermsg.ip = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e3) {
			e3.printStackTrace();
		}
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//上线时触发,发送用户信息(usermsg)给服务器
		jb1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String ipaddress = jtfname2.getText(); // 得到服务器IP
					try {
						if (flag == false) {
						s = new Socket(ipaddress, 9999);
						oos = new ObjectOutputStream(s.getOutputStream());
						flag = true;
						}
					} catch (UnknownHostException e2) {
						e2.printStackTrace();
					} catch (IOException e2) {
						e2.printStackTrace();
					}
					try {
						usermsg.username = jtfname.getText();
						usermsg.password = jtfpwd.getText();
						usermsg.flag1 = true; // 登陆是设为true
						oos.writeObject(usermsg);
						oos.reset();
					} catch (UnknownHostException e1) {
						e1.printStackTrace();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
					ClientThread ct = new ClientThread();
					ct.start();
				}
		});
//		  监听用户下线信息
		jfb.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				Usermessage usermsg1 = new Usermessage();
				usermsg1.flag1 = false; // 表明用户下线
				usermsg1.username = usermsg.username;
				usermsg1.password = usermsg.password;
				try {
					usermsg1.ip = InetAddress.getLocalHost().getHostAddress();
				} catch (UnknownHostException e2) {
					e2.printStackTrace();
				}
				// 将用户信息发给服务器说明该客户要下线
				try {
					oos.writeObject(usermsg1);
					System.exit(0);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
//		  监听双击在线用户列表事件
		jli.addMouseListener(new MouseListener() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					JList source = (JList) e.getSource();
					Object[] selection = source.getSelectedValues();
					cmd = (String) selection[0];
					String[] ss = cmd.split("\s+");
					jfaa.setTitle("与" + ss[0] + "聊天");
					jfaa.setVisible(true);
				}
			}
			public void mouseEntered(MouseEvent e) {
			}
			public void mouseExited(MouseEvent e) {
			}
			public void mousePressed(MouseEvent e) {
			}
			public void mouseReleased(MouseEvent e) {
			}
		});
		//监听私聊send按钮
		jbaa.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ClientUtil cu = new ClientUtil();
				Message m = new Message();
				m.fromname = usermsg.username;
				m.msg = jtabb.getText();
				String[] ss = cmd.split("\s+");
				m.toname = ss[0];
				m.flag = true;
				try {
					oos.writeObject(m);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				jtabb.setText("");
//				 调用ClientUtil中的dealmessage()方法对信息进行处理
				ArrayList msglist = ClientUtil.dealmessage(m.msg);
				// 调用ClientUtil中的printmsg()将信息显示到面板上
				cu.printmsg(msglist, jtaaa,m);
			}
		});
		 // 监听群聊send按钮
		jba.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Message m = new Message();
				m.fromname = usermsg.username;
				m.msg = textPane1.getText();
				m.toname = "alluser";
				try {
					oos.writeObject(m);
				} catch (IOException e1) {
					// e1.printStackTrace();
				}
				textPane1.setText("");
			}
		});
		// 监听Transportfile按钮,实现文件传输,只能传输小文件
		jbbc.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				FileDialog f = new FileDialog(jfaa, "文件传送", FileDialog.LOAD);
				f.setVisible(true);
				String filename = f.getDirectory() + f.getFile();
				File file = new File(filename);
				FileInputStream fis;
				try {
					fis = new FileInputStream(file);
					int len = (int) file.length();
					byte[] data = new byte[len];
					fis.read(data);
					Message m = new Message();
					m.data = new byte[len];
					m.fromname = usermsg.username;
					String[] ss = cmd.split("\s+");
					m.toname = ss[0];
					m.flag = false;
					for (int i = 0; i < data.length; i++) {
						m.data[i] = data[i];
					}
					oos.writeObject(m);
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				} catch (IOException e2) {
					e2.printStackTrace();
				}
			}
		});
		// 监听"群聊"按钮
		jb.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				jfa.setVisible(true);
			}
		});
	}
	 //显示在线用户列表(由服务器传入)
	class ClientThread extends Thread {
		ObjectInputStream ois;
		public void run() {
			ClientUtil cu = new ClientUtil();
			try {
				ois = new ObjectInputStream(s.getInputStream());
				while (true) {
					Object[] o = (Object[]) ois.readObject();
					// 处理“用户名重名”信息
					if (o[0] instanceof String) {
						String s1 = (String) o[0];
						if (s1.equals("用户名重复请重新填写")) {
							jtfname3.setText(s1);
						} else {
							jfb.setTitle(jtfname.getText());
							jfb.setVisible(true);
							jf.setVisible(false);
						}
					}
					// 处理用户信息
					if (o[0] instanceof Usermessage) {
						Usermessage u = (Usermessage) o[0];
						// 处理用户上线信息
						if (u.flag1) {
							dlm.clear();
							for (int i = 0; i < o.length; i++) {
								dlm.addElement(o[i].toString());
							}
						} else {
							// 处理用户下线信息
							for (int i = 0; i < dlm.size(); i++) {
								String s = dlm.get(i).toString();
								String[] ss = s.split("\s+");
								if (ss[0].equals(u.username)) {
									dlm.remove(i);
									break;
								}
							}
						}
					}
					// 处理聊天信息
					if (o[0] instanceof Message) {
						Message u = (Message) o[0];
						// 处理群聊信息
						if (u.toname.equals("alluser")) {
							// 调用ClientUtil中的dealmessage()方法对信息进行处理
							ArrayList msglist = ClientUtil.dealmessage(u.msg);
							// 调用ClientUtil中的printmsg()将信息显示到面板上
							cu.printmsg(msglist, textPane,u);
						}
						// 处理私聊信息
						if (u.toname.equals(usermsg.username)) {
							if (u.flag) {
								jfaa.setVisible(true);
								jfaa.setTitle("与" + u.fromname + "聊天");
//								 调用ClientUtil中的dealmessage()方法对信息进行处理
								ArrayList msglist = ClientUtil.dealmessage(u.msg);
								// 调用ClientUtil中的printmsg()将信息显示到面板上
								cu.printmsg(msglist, jtaaa,u);
								cmd = u.fromname;
							} else {
//								处理接收文件
								FileDialog f = new FileDialog(jfaa,
										"文件接收", FileDialog.SAVE);
								f.setVisible(true);
								String filename = f.getDirectory()
										+ f.getFile();
								FileOutputStream fos = new FileOutputStream(
										filename);
								byte[] buf = u.data;
								fos.write(buf);
								fos.close();
							}
						}
					}
				}
			} catch (IOException e) {
				 e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		Client c = new Client();
	}
}

Clientframe.java:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import sun.awt.image.ToolkitImage;
import sun.reflect.generics.scope.Scope;
/**
 *
 * @author tian
 *
 */
public class Clientframe {
	//	登陆窗口
	JFrame jf;
	JPanel jp1;
	JPanel jp2;
	JLabel jl1;
	JLabel jl2;
	JLabel jl3;
	JLabel jl4;
	JLabel jl5;
	JLabel jl6;
	JLabel jl7;
	Checkbox cb;
	Checkbox cb2;
	Choice ci ;
	ImageIcon im;
	JLabel jl8;
	JTextField jtfname;
	JPasswordField jtfpwd;
	JTextField jtfname2;
	JTextField jtfname3;
	JButton jb1;
	JButton jb2;
//	  群聊窗口
	JFrame jfa;
	JPanel jpa;
	JPanel jp;
	JPanel jpl;
	TextArea jtaa;
	TextArea jtab;
	JButton jba;
	JButton jbb;
	JButton jbc;
	JTextPane textPane;
	JTextPane textPane1;
	ImageIcon iic;
//	  私聊窗口
	JFrame jfaa;
	JPanel jpaa;
	JPanel jpp;
	JPanel jpll;
	JTextPane jtaaa;
	JTextPane jtabb;
	JButton jbaa;
	JButton jbbb;
	JButton jbbc;
	ImageIcon iic2;
//	  上线用户列表
	JFrame jfb;
	JPanel jpan;
	JPanel jpan1;
	DefaultListModel dlm;
	JList jli;
	JButton jb;
	ImageIcon ii;
	ImageIcon iii;
	JLabel jl;
//	群聊表情窗口
	JFrame jfc;
	JPanel jpbq;
	/**
	 *
	 */
	public Clientframe(){
//		初始化群聊表情窗口
		jfc = new JFrame("表情");
		jpbq = new JPanel();
		jfc.add(jpbq, BorderLayout.CENTER);
		jpbq.setLayout(new GridLayout(5,5));
		jfc.add(jpbq);
		URL qqurl = this.getClass().getClassLoader().getResource("Image/qq.jpg");
		jfc.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl));
		ArrayList al = new ArrayList();
		for (int i = 10; i < 35; i++) {
			URL url = this.getClass().getClassLoader().getResource( "Image/"+i+".gif");
			ImageIcon ii=new ImageIcon(url);
			JLabel jl = new JLabel(ii);
			al.add(jl);
		}
		for (int j = 0; j < al.size(); j++) { 			JLabel jl = (JLabel) al.get(j); 			jl.addMouseListener(new MouseListener(){ 				public void mouseClicked(MouseEvent e) { 					StyledDocument doc = textPane1.getStyledDocument(); 					SimpleAttributeSet attr = new SimpleAttributeSet(); 					StyleConstants.setForeground(attr, Color.red); 					String s = e.getSource().toString(); 					Pattern p = Pattern.compile(".gif"); 					Matcher m = p.matcher(s); 					while(m.find()){ 							int start = m.start(); 							int end = m.end(); 							String subs = s.substring(start-2,end); 							String path = "d:\"+subs; 							textPane1.setCaretPosition(doc.getLength()); // 设置插入位置 							File file = new File(path); 							Icon image = new ImageIcon(file.getAbsoluteFile().toString()); 							textPane1.insertIcon(image); 							try { 								doc.insertString(doc.getLength(), file.getName(), attr); 							} catch (BadLocationException e1) { 								 								e1.printStackTrace(); 							} 						} 				} 				public void mouseEntered(MouseEvent e) { 					 				} 			 				public void mouseExited(MouseEvent e) { 					 				} 				public void mousePressed(MouseEvent e) { 					 				} 				public void mouseReleased(MouseEvent e) { 					 				} 			}); 			jpbq.add(jl); 		} 		 		jfc.setVisible(false); 		jfc.setSize(250,250); 		 //		初始化登陆窗口 		 		jf = new JFrame("QQ用户登陆"); 		jp1 = new JPanel(); 		jp2 = new JPanel(); 		jtfname = new JTextField("",15); 		jtfpwd = new JPasswordField("",15); 		jtfname2 = new JTextField("",15); 		jl1 = new JLabel("QQ账号:"); 		jl2 = new JLabel("QQ密码:"); 		jl3 = new JLabel("ServerIP:"); 		jl4 = new JLabel("申请账号"); 	    jl5 = new JLabel("忘了密码"); 		jl6 = new JLabel("QQ状态:"); 		jl7=new JLabel("动感地带 "); 		cb = new Checkbox("自动登录"); 	    cb2 = new Checkbox("记住密码"); 		ci = new Choice(); 		jb1 = new JButton("登录");         jb2 = new JButton("退出");         URL jmurl =this.getClass().getClassLoader().getResource("Image/jm.jpg");         im = new ImageIcon(jmurl);         jl8=new JLabel(im); 		jf.add(jp1,BorderLayout.CENTER); 		jf.add(jp2,BorderLayout.SOUTH); 		jp1.add(jl8); 		jp1.add(jl1); 		jp1.add(jtfname); 		jp1.add(jl4); 		jp1.add(jl2); 		jp1.add(jtfpwd); 		jp1.add(jl5); 		jp1.add(jl3); 		jp1.add(jtfname2); 		jp1.add(jl7); 		jp1.add(jl6); 		jp1.add(ci); 		jp1.add(cb2); 		jp1.add(cb); 		jp2.add(jb1); 		jp2.add(jb2); 		jf.setBackground(new Color(100, 253, 98)); 		jp1.setBackground(new Color(176, 224, 230 )); 		jp2.setBackground(new Color(127, 255, 212 )); 		ci.add("在线"); 		ci.add("Q我吧"); 		ci.add("忙碌"); 		ci.add("离开"); 		ci.add("静音"); 		ci.add("隐身"); 		jl1.setForeground(Color.BLUE); 		jl2.setForeground(Color.BLUE); 		jl3.setForeground(Color.BLUE); 		jl4.setForeground(Color.BLUE); 		jl5.setForeground(Color.BLUE); 		jl7.setForeground(Color.BLUE); 		jb1.setSize(20, 10); 		jb2.setSize(20, 10);         jf.setSize(333, 250); 		jf.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl)); 		jf.setLocation(500, 250); 		jf.setResizable(false); 		jf.setVisible(true); 		 		jb2.addActionListener(new ActionListener(){ 			public void actionPerformed(ActionEvent e) { 				System.exit(1); 				 			} 			 		}); 		 //		初始化群聊窗口 		jfa = new JFrame("QQ聊天室"); 		jpa = new JPanel(); 		jp = new JPanel(); 		jpl = new JPanel(); 		dlm = new DefaultListModel(); 		URL iicurl =this.getClass().getClassLoader().getResource("Image/12.gif"); 		iic=new ImageIcon(iicurl); 		textPane = new JTextPane(); 		textPane1 = new JTextPane(); 		textPane.setPreferredSize(new Dimension(360, 200)); 		textPane1.setPreferredSize(new Dimension(360, 100)); 		textPane.setFont(new Font("Serif",Font.BOLD,14)); 		textPane1.setFont(new Font("Serif",Font.BOLD,14)); 		JScrollPane scrollPane = new JScrollPane(textPane); 		JScrollPane scrollPane1 = new JScrollPane(textPane1); 		textPane1.setForeground(Color.red); 		jba = new JButton("发送"); 		jbb = new JButton("清空"); 		jbc = new JButton("表情",iic); 		jbc.setMnemonic(KeyEvent.VK_F);//给“表情”按钮设置快捷键 		jba.setForeground(Color.BLUE); 		jbb.setForeground(Color.BLUE); 		jbc.setForeground(Color.BLUE); 		jfa.add(jpa, BorderLayout.NORTH); 		jfa.add(jp, BorderLayout.CENTER); 		jfa.add(jpl, BorderLayout.SOUTH); 		jfa.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl)); 		jpa.add(scrollPane); 		jp.add(scrollPane1); 		jpl.add(jba); 		jpl.add(jbc); 		jpl.add(jbb); 		jpa.setBackground(new Color(100, 253, 98)); 		jp.setBackground(new Color(100, 253, 98)); 		jpl.setBackground(new Color(100, 253, 98)); 		jfa.setSize(380, 400); 		jfa.setLocation(500, 150); 		jfa.setResizable(false); 		jfa.setVisible(false); 		jbb.addActionListener(new ActionListener() { 			public void actionPerformed(ActionEvent e) { 				textPane1.setText(""); 			} 		}); 		 		 //		 监听群聊表情按钮 		jbc.addActionListener(new ActionListener() { 			public void actionPerformed(ActionEvent e) { 				jfc.setVisible(true); 				jfc.setLocation(500, 150); 			} 		}); 		 //		初始化私聊窗口 		jfaa = new JFrame("私聊");			 		jpaa = new JPanel();			 		jpp = new JPanel(); 		jpll =new JPanel(); 		URL transcurl =this.getClass().getClassLoader().getResource("Image/trans.gif"); 		iic2 =new ImageIcon(transcurl); 		jtaaa = new JTextPane();	 		jtabb = new JTextPane(); 		jtaaa.setPreferredSize(new Dimension(360, 200)); 		jtabb.setPreferredSize(new Dimension(360, 100)); 		JScrollPane scrollPanea = new JScrollPane(jtaaa); 		JScrollPane scrollPaneb = new JScrollPane(jtabb); 		jtabb.setForeground(Color.red); 		jbaa = new JButton("发送");		 		jbbb = new JButton("清空");		 		jbbc = new JButton("",iic2); 		jbc.setMnemonic(KeyEvent.VK_F); 		jtaaa.setFont(new Font("Serif",Font.BOLD,14)); 		jtabb.setFont(new Font("Serif",Font.BOLD,14)); 	    jbaa.setForeground(Color.BLACK); 		jbbb.setForeground(Color.BLACK); 		jbbc.setForeground(Color.BLACK); 		jfaa.add(jpaa,BorderLayout.NORTH); 		jfaa.add(jpp,BorderLayout.CENTER); 		jfaa.add(jpll,BorderLayout.SOUTH); 		jfaa.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl)); 		jpaa.add(scrollPanea); 		jpp.add(scrollPaneb); 		jpll.add(jbaa); 		jpll.add(jbbc); 		jpll.add(jbbb); 		jf.setBackground(new Color(100, 253, 98)); 		jpaa.setBackground(new Color(100, 253, 98));		 		jpp.setBackground(new Color(100, 253, 98));		  		jpll.setBackground(new Color(100, 253, 98));		  		jfaa.setSize(380, 400); 		jfaa.setLocation(500, 150); 		jfaa.setResizable(false); 		jfaa.setVisible(false);	 		 	    		jbbb.addActionListener(new ActionListener(){ 			public void actionPerformed(ActionEvent e) { 				jtabb.setText(""); 			} 			 		}); 		 //		初始化上线用户列表 		jfb = new JFrame(); 		jpan = new JPanel(); 		dlm = new DefaultListModel(); 		jli = new JList(dlm); 		jb = new JButton(">>>群聊模式");
		jli.setForeground(Color.BLUE);
		jb.setSize(15, 20);
		jb.setForeground(Color.BLUE);
		jfb.add(jpan);
		jpan.setLayout(new BorderLayout());
		jpan.add(jli, BorderLayout.CENTER);
		jpan.add(jb, BorderLayout.SOUTH);
		jpan.add(jli);
		jfb.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl));
		jfb.setResizable(false);
		jfb.setLocation(500, 200);
		jfb.setSize(200, 450);
		jfb.setVisible(false);
	}
}

ClientUtil.java:

import java.awt.Color;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class ClientUtil {
	/**
	 * 处理已接收信息,将表情与字符分开
	 */
	public static ArrayList dealmessage(String message) {
		String msg = message;
		// 处理字符串
		ArrayList list = new ArrayList();
		ArrayList substrlist = new ArrayList();
		list.add(0);
		Pattern p = Pattern.compile(".gif");
		Matcher m = p.matcher(msg);
		while (m.find()) {
			int start = m.start() - 2;
			list.add(start);
			start = start + 6;
			if (start > msg.length()) {
				break;
			}
			list.add(start);
		}
		for (int i = 0; i < list.size(); i++) {
			if (i < list.size()-1) {
				int begin = Integer.parseInt(list.get(i).toString());
				int over = Integer.parseInt(list.get(i + 1).toString());
				String subs = msg.substring(begin, over);
				substrlist.add(subs);
			}else{
				String subs = msg.substring(Integer.parseInt(list.get(i).toString()));
				substrlist.add(subs);
			}
		}
		return substrlist;
	}
	/**
	 * 将处理过的信息显示在聊天面板上
	 */
	public void printmsg(ArrayList list,JTextPane tPane,Message u){
		Icon image = null;
		JTextPane textPane = tPane;
		StyledDocument doc = textPane.getStyledDocument();
		SimpleAttributeSet attr = new SimpleAttributeSet();
		StyleConstants.setForeground(attr, Color.red);
		ArrayList msglist = list;
		Pattern p = Pattern.compile(".gif");
		try {
			doc.insertString(doc.getLength(), u.toString() ,
					attr);
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		}
		for (int i = 0; i < msglist.size(); i++) {
			String sub = msglist.get(i).toString();
			Matcher m = p.matcher(sub);
			if(m.find()){
				URL url = this.getClass().getClassLoader().getResource("Image/" + sub);
				textPane.setCaretPosition(doc.getLength()); // 设置插入位置
				image = new ImageIcon(url);
				textPane.insertIcon(image);
			}else{
				try {
					doc.insertString(doc.getLength(), sub ,
							attr);
				} catch (BadLocationException e) {
					e.printStackTrace();
				}
			}
		}
		try {
			doc.insertString(doc.getLength(), "n" ,
					attr);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
	}
}

Message.java:

import java.io.Serializable;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
/**
 *
 * @author tian
 *	在线用户之间的聊天信息
 */
public class Message implements Serializable{
	public String fromname;
	public String toname;
	public String msg;
	public boolean flag;
	byte[] data = null;//用于传送文件
	int len;   //信息会随着文件的长度而变化
	public Message(){
	}
	public int getLen() {
		return len;
	}
	public void setLen(int len) {
		this.len = len;
	}
	public String toString() {
		Date today = new Date();
		Locale cnLocale = new Locale("zh", "CN");
		DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, cnLocale);
		return fromname+" 于"+df.format(today)+"说:"+"n";
	}
}

Server.java:

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
 *
 * @author tian	QQ服务器端
 *
 */
public class Server {
	JFrame jf = new JFrame("QQ服务器");
	JPanel jp = new JPanel();
	JPanel jp1 = new JPanel();
	JTextArea jta = new JTextArea();
	JTextArea jta1 = new JTextArea();
	JScrollPane jsp = new JScrollPane(jta1);
	JLabel jl = new JLabel("QQ在线列表");
	protected ServerSocket ss;
	DefaultListModel dlm = new DefaultListModel();
	JList jli = new JList(dlm);
	ArrayList uml = new ArrayList();
	HashMap mp = new HashMap();		//存储socket对应的oos
	HashMap mp1 = new HashMap();	//存储用户所对应的socket
//	 初始化服务器
	public Server() {
		jf.add(jp, BorderLayout.NORTH);
		jp.setLayout(new FlowLayout(FlowLayout.LEFT));
		jp.add(jl);
		jf.add(jli, BorderLayout.CENTER);
		jf.add(jsp, BorderLayout.SOUTH);
		jta1.setSize(250, 300);
		jsp.setSize(250, 300);
		jf.setSize(250, 500);
		jf.setLocation(500, 150);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
		try {
			ss = new ServerSocket(9999);
			Thread t = new ServerThread();
			t.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 *
	 * 监听线程
	 *
	 */
	class ServerThread extends Thread {
		public void run() {
			try {
				while (true) {
					Socket sc = ss.accept();
					ObjectOutputStream oos = new ObjectOutputStream(sc
							.getOutputStream());
					mp.put(sc, oos);
					SocketThread st = new SocketThread(sc);
					st.start();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 *
	 * 读线程
	 *
	 */
	class SocketThread extends Thread {
		Socket sc;
		public SocketThread(Socket sc) {
			this.sc = sc;
		}
		public void run() {
			try {
				ObjectInputStream ois = new ObjectInputStream(sc
						.getInputStream());
				while (true) {
					Object m = ois.readObject();
					// 处理用户信息
					if (m instanceof Usermessage) {
						Usermessage u = (Usermessage) m;
						// 如果flag1为true,则是登陆信息
						if (u.flag1 && Server.checkuser(u,uml,mp)) {
							mp1.put(u.username, sc);
							dlm.addElement(sc.getRemoteSocketAddress() + "n");
							uml.add(u);
							Object[] o = uml.toArray();
							Iterator i = mp.keySet().iterator();
							while (i.hasNext()) {
								Socket sc = (Socket) i.next();
								((ObjectOutputStream) mp.get(sc))
										.writeObject(o);
							}
						} else if(!u.flag1){
							ois.close();
							ObjectOutputStream oos = (ObjectOutputStream) mp
									.get(sc);
							oos.close();
							sc.close();
							mp.remove(sc);
							for (int i = 0; i < uml.size(); i++) { // 通过for循环可以遍历ArrayList
								Usermessage um = (Usermessage) uml.get(i); // 通过下标输出ArrayList里面的值
								if (um.username.equals(u.username)) {
									uml.remove(i);
									break;
								}
							}
							Object[] o = new Object[1];
							o[0] = u;
							Iterator i = mp.keySet().iterator();
							while (i.hasNext()) {
								Socket sc = (Socket) i.next();
								((ObjectOutputStream) mp.get(sc))
										.writeObject(o);
							}
						}
					}
					// 处理聊天信息
					if (m instanceof Message) {
						Message u = (Message) m;
						//处理群聊
						if (u.toname.equals("alluser")) {
							ArrayList al = new ArrayList();
							al.add(u);
							Object[] o1 = al.toArray();
							Iterator i = mp.keySet().iterator();
							while (i.hasNext()) {
								Socket sc = (Socket) i.next();
								((ObjectOutputStream) mp.get(sc))
										.writeObject(o1);
							}
						}else{
							//处理私聊和文件传送
							ArrayList al = new ArrayList();
							al.add(u);
							Object[] o1 = al.toArray();
							((ObjectOutputStream) mp.get(mp1.get(u.toname))).writeObject(o1);
						}
					}
				}
			} catch (IOException e1) {
//				e1.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	//检测用户名是否重复
	public static boolean checkuser(Usermessage u,ArrayList uml,HashMap mp){
		ArrayList uml1 = uml;
		int i;
		for (i = 0; i < uml1.size(); i++) { // 通过for循环可以遍历ArrayList 			Usermessage um = (Usermessage) uml1.get(i); // 通过下标输出ArrayList里面的值 			if (um.username.equals(u.username)) { 				String s = "用户名重复请重新填写"; 				Object[] o = new Object[1]; 				o[0] = s; 				Iterator it = mp.keySet().iterator(); 				while (it.hasNext()) { 					try { 						Socket sc = (Socket) it.next(); 						((ObjectOutputStream) mp.get(sc)) 								.writeObject(o); 					} catch (IOException e) { 						e.printStackTrace(); 					} 				} 			break;	 			} 		} 		if(i>=uml1.size()){
		String s = "success";
		Object[] o = new Object[1];
		o[0] = s;
		Iterator it = mp.keySet().iterator();
		while (it.hasNext()) {
			try {
				Socket sc = (Socket) it.next();
				((ObjectOutputStream) mp.get(sc))
						.writeObject(o);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return true;
		}else{
			return false;
		}
	}
	public static void main(String[] args) {
		Server s = new Server();
	}
}

Usermessage.java:

import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
/**
 *
 * @author tian
 *	用于传递用户信息
 */
public class Usermessage implements Serializable{
	public String username;		//用户名称
	public String password;		//用户密码
	public String ip;			//用户IP
	public boolean flag1;		//标志用户上线还是下线,为true表示为登陆
	public String ipaddress;	//服务器IP
	public Usermessage(){
	}
	public boolean equals(Usermessage user) {
		boolean flag= false;
		if(ip.equals(user.ip)){
			flag = true;
		}
		return flag;
	}
	public String toString() {
		return username + "  "+ ip;
	}
}

打包好的程序和图片资源下载地址:
http://download.csdn.net/detail/xionglong/1638561
效果:



表情窗口