java实现c/s客户端间聊天-凯发app官方网站

凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 405140
  • 博文数量: 121
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1393
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-11 12:17
个人简介

www.vibexie.com vibexie@qq.com

文章分类

(121)

  • (8)
  • (10)
  • (20)
  • (1)
  • (8)
  • (11)
  • (0)
  • (26)
  • (4)
  • (2)
  • (1)
  • (13)
  • (0)
  • (3)
  • (14)
  • (0)
文章存档

(55)

(66)

我的朋友
相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: java

2015-03-22 15:33:08

用了一天半的时间,终于还是把客户端聊天的功能实现了,刚开始的时侯,考虑客户端处于内网和外网的因素,肯定不能用udp通过ip地址来传送消息,
不过也可以通过udp打洞的方式来实现穿透nat,qq就是用udp打洞的方式,不过还有先进的方法保证udp的可靠性。但是udp打洞也是很难实现的。
经过半天的思考,还是决定用一种简单的方法实现 客户端-服务器-客户端 的通信,基于socket,在服务端保存一张map,map键值分别为username,socket。
再通过服务器的并发实现客户端间的通信,客户端发送的所有数据都先传到服务器,服务器再把数据解析发送到对应的接收端。

架构是这样的:
    客户端连接服务器,服务端start一个服务线程,用户输入自己的名字,发送给服务器端,服务器将username和当前连接的socket保存到一张hashmap中。
    如果已经有用户使用了该名字,服务器查询map中已经有这个用户吗,有的话通知用户重新输入用户名。
    确定username后,再选择要发送信息的好友,服务器再从map中拿出好友的socket,再把消息转发至好友的socket
    客户端接收方面,启用一个后台进程,去read自己的socket获取消息。
    更重要的是,后台线程和客户端主线程间用一个单向的管道,传送一些必要的信息。
整个架构中都没有忙等待,都是通过阻塞的方式进行数据的传递。所以在性能上是很强的。

由于只是为了实现这个功能,代码写好后就没有改过,代码还是挺粗糙的,经过测试,剩下一个bug就是linux端和windows端进行中文通信的时候就会有乱码,这个就不去处理了。

附源码:
     
给出服务器端代码:

qqserver.java

  1. package cn.com.xiebiao.smallqqserver;

  2. import java.io.ioexception;
  3. import java.net.serversocket;
  4. import java.net.socket;

  5. /**
  6.  *
  7.  * title : qqserver.java 
  8.  * author : vibe xie @
  9.  * time : mar 22, 2015 3:24:07 pm
  10.  * 凯发app官方网站 copyright: 凯发app官方网站 copyright (c) 2015
  11.  * description:
  12.  */

  13. public class qqserver {
  14.     private static serversocket serversocket=null;
  15.     private static socket socket=null;
  16.     //服务器端口
  17.     private static int server_port=8999;
  18.     //服务次数
  19.     private static int server_times=1;
  20.     
  21.     public static void main(string[] args) {
  22.         // todo auto-generated method stub    
  23.         try {
  24.             serversocket=new serversocket(server_port);
  25.             system.out.println("qq服务器启动...");
  26.             while(true){
  27.                 socket=serversocket.accept();
  28.                 system.out.println("服务器第"(server_times)"次连接");
  29.                 new thread(new serverthread(socket)).start();
  30.             }
  31.             
  32.         } catch (ioexception e) {
  33.             // todo auto-generated catch block
  34.             e.printstacktrace();
  35.         }finally{
  36.             try {
  37.                 serversocket.close();
  38.             } catch (ioexception e) {
  39.                 // todo auto-generated catch block
  40.                 e.printstacktrace();
  41.             }
  42.         }

  43.     }
  44. }

serverthread.java

  1. package cn.com.xiebiao.smallqqserver;

  2. import java.io.bufferedinputstream;
  3. import java.io.bufferedreader;
  4. import java.io.bufferedwriter;
  5. import java.io.ioexception;
  6. import java.io.inputstreamreader;
  7. import java.io.outputstreamwriter;
  8. import java.net.socket;
  9. import java.util.iterator;
  10. import java.util.map;
  11. import java.util.set;

  12. /**
  13.  *
  14.  * title : clientthread.java 
  15.  * author : vibe xie @
  16.  * time : mar 21, 2015 2:34:04 pm
  17.  * 凯发app官方网站 copyright: 凯发app官方网站 copyright (c) 2015
  18.  * description: clientthread接收发送端发来的消息,再把消息通过writethead线程发送给接收端
  19.  */
  20. public class serverthread implements runnable {
  21.     private socket socket;
  22.     private boolean flag=true;
  23.     //msg的格式是 "senderreceiver" 正文
  24.     private string msg=null;
  25.     private msganalyseutil msganalyse;
  26.     //将msg拆解为sender,receiver,message
  27.     private string sender;
  28.     private string receiver;
  29.     private string message;
  30.     
  31.     private bufferedreader bufferedreader;
  32.     private bufferedwriter writer;
  33.     public serverthread(socket socket) {
  34.         // todo auto-generated constructor stub
  35.         this.socket=socket;
  36.     }
  37.     
  38.     @override
  39.     public void run() {
  40.         // todo auto-generated method stub
  41.         try {
  42.             
  43.             /******************登录验证模块,用户名已存在,则放回fail,否则返回success************************/
  44.             bufferedreader=new bufferedreader(new inputstreamreader(socket.getinputstream()));
  45.             writer=new bufferedwriter(new outputstreamwriter(socket.getoutputstream()));
  46.             //连接成功则从socket读取用户名
  47.             sender=bufferedreader.readline();
  48.             //用户已存在,返回登录失败信息
  49.             while(usertable.isuserexist(sender)==true){
  50.                 writer.write("fail");
  51.                 writer.newline();
  52.                 writer.flush();
  53.                 sender=bufferedreader.readline();
  54.             }
  55.             
  56.             //将用户名和socket保存到用户表中
  57.             usertable.useradd(sender, socket);
  58.             //收到的用户名不存在,返回登录成功
  59.             writer.write("success");
  60.             writer.newline();
  61.             writer.flush();
  62.             /******************登录验证模块*************************************************************/
  63.             
  64.             while(flag==true){
  65.                 msg=bufferedreader.readline();
  66.                 //进行拆解
  67.                 system.out.println("服务器接收"msg);
  68.                 msganalyse=new msganalyseutil(msg);
  69.                 sender=msganalyse.getsender();
  70.                 receiver=msganalyse.getreceiver();
  71.                 message=msganalyse.getmessage();
  72.                 
  73.                 //接收到验证好友是否在线指令
  74.                 if(sender.equals("/instruction")){
  75.                     //在usertable表中查询好友是否在线,并重新包装消息,返回给请求者
  76.                     if(usertable.isuserexist(receiver)){
  77.                         message="y";
  78.                     }else {
  79.                         message="n";
  80.                     }
  81.                     //包装消息
  82.                     msg=sender"""tmp"message;
  83.                     writer.write(msg);
  84.                     writer.newline();
  85.                     writer.flush();
  86.                 }else{
  87.                     //未接收到指令,进行消息转发
  88.                     //服务器输出消息
  89.                     system.out.println("解析为 发送者:"sender" 接收者:"receiver" 信息:"message);
  90.                     
  91.                     if(message.equals("bye")){
  92.                         system.out.println("用户退出");
  93.                         
  94.                         //退出是通知客户端后台线程结束
  95.                         msg="/instruction""""tmp""bye";
  96.                         writer.write(msg);
  97.                         writer.newline();
  98.                         writer.flush();
  99.                         
  100.                         //从usertable中删除该用户
  101.                         usertable.deleteuser(sender);
  102.                         
  103.                         //结束服务器线程
  104.                         flag=false;
  105.                     }else {
  106.                         //转发消息给接收者
  107.                         writer=new bufferedwriter(new outputstreamwriter(usertable.getsocket(receiver).getoutputstream()));
  108.                         writer.write(msg);
  109.                         writer.newline();
  110.                         writer.flush();
  111.                     }
  112.                 }
  113.             }
  114.         } catch (exception e) {
  115.             // todo: handle exception
  116.             e.printstacktrace();
  117.         }
  118.         
  119.     }
  120. }

usertable.java

  1. package cn.com.xiebiao.smallqqserver;

  2. import java.net.socket;
  3. import java.util.hashmap;
  4. import java.util.map;
  5. /**
  6.  *
  7.  * title : usertable.java 
  8.  * author : vibe xie @
  9.  * time : mar 22, 2015 3:25:35 pm
  10.  * 凯发app官方网站 copyright: 凯发app官方网站 copyright (c) 2015
  11.  * description:
  12.  */
  13. class usertable{
  14.     private static map<string, socket> usertable=new hashmap<string, socket>();
  15.     
  16.     public static void useradd(string user,socket socket){
  17.         usertable.put(user,socket);
  18.     }
  19.     
  20.     public static void deleteuser(string user){
  21.         for(map.entry<string,socket> entry:usertable.returnusertable().entryset()){
  22.             if(entry.getkey().equals(user)){
  23.                 usertable.remove(entry.getkey());
  24.             }
  25.         }
  26.     }
  27.     
  28.     public static boolean isuserexist(string user){
  29.         boolean flag=false;
  30.         for(map.entry<string,socket> entry:usertable.returnusertable().entryset()){
  31.             if(entry.getkey().equals(user)){
  32.                 flag=true;
  33.                 return flag;
  34.             }
  35.         }
  36.         return flag;
  37.     }
  38.     public static map<string , socket> returnusertable(){
  39.         return usertable;
  40.     }
  41.     public static socket getsocket(string user){
  42.         return usertable.get(user);
  43.     }

  44. }


msganalyseutil.java

  1. package cn.com.xiebiao.smallqqserver;

  2. import java.util.regex.pattern;
  3. /**
  4.  *
  5.  * title : msganalyseutil.java 
  6.  * author : vibe xie @
  7.  * time : mar 21, 2015 3:35:16 pm
  8.  * 凯发app官方网站 copyright: 凯发app官方网站 copyright (c) 2015
  9.  * description:分析message的工具类
  10.  */
  11. public class msganalyseutil {
  12.     private string msg;
  13.     private string sender;
  14.     private string receiver;
  15.     private string message;
  16.     string[] tmp=new string[3];
  17.     
  18.     public msganalyseutil(string msg){
  19.         this.msg=msg;
  20.         tmp=pattern.compile("").split(msg,3);
  21.         sender=tmp[0];
  22.         receiver=tmp[1];
  23.         message=tmp[2];
  24.     }
  25.     
  26.     public string getsender() {
  27.         return sender;
  28.     }
  29.     
  30.     public string getreceiver() {
  31.         return receiver;
  32.     }
  33.     
  34.     public string getmessage() {
  35.         return message;
  36.     }
  37. }

客户端代码:

qqclient.java

  1. package cn.com.xiebiao.smallqqclient;

  2. import java.io.bufferedreader;
  3. import java.io.bufferedwriter;
  4. import java.io.ioexception;
  5. import java.io.inputstreamreader;
  6. import java.io.outputstreamwriter;
  7. import java.io.pipedreader;
  8. import java.net.socket;
  9. /**
  10.  *
  11.  * title : qqclient.java 
  12.  * author : vibe xie @
  13.  * time : mar 22, 2015 3:27:29 pm
  14.  * 凯发app官方网站 copyright: 凯发app官方网站 copyright (c) 2015
  15.  * description:
  16.  */
  17. public class qqclient {
  18.     //服务器域名
  19.     private static string domain="localhost";
  20.     //服务器端口
  21.     private static int server_port=8999;
  22.     //socket
  23.     private static socket socket;
  24.     //发送者
  25.     private static string sender;
  26.     //接收者
  27.     private static string recerver;
  28.     //消息
  29.     private static string msg;
  30.     //用户是否想退出
  31.     private static string isloginout="n";
  32.     //接收后台返回好友是否存在指令的管道
  33.     private static pipedreader pipedreader;
  34.     //好友是否在线
  35.     private static boolean isfriendonline=false;
  36.     public static void main(string[] args) {
  37.         // todo auto-generated method stub
  38.         try{
  39.             socket=new socket(domain,server_port);
  40.             
  41.             bufferedreader in=new bufferedreader(new inputstreamreader(system.in));
  42.             bufferedwriter writer=new bufferedwriter(new outputstreamwriter(socket.getoutputstream()));
  43.             //reader仅仅为了用户验证,不用作后台接收消息
  44.             bufferedreader reader=new bufferedreader(new inputstreamreader(socket.getinputstream()));
  45.             boolean flag=true;
  46.             
  47.             system.out.printf("/********************************************\n"
  48.                              "/*************smallqq version1.0*************\n"
  49.                              "/*************@author vibexie *************\n"
  50.                              "/*************@email vibexie@qq.com**********\n"
  51.                              "/********************************************\n");
  52.             /******************登录验证模块,用户名已存在,则放回fail,否则返回success************************/
  53.             //开启客户端,发送用户名
  54.             system.out.print("请输入用户名(临时):");
  55.             sender=msg=in.readline();
  56.             writer.write(msg);
  57.             writer.newline();
  58.             writer.flush();
  59.             while(reader.readline().equals("fail")){
  60.                 system.out.print("用户已存在,请输入用户名(临时):");
  61.                 sender=msg=in.readline();
  62.                 writer.write(msg);
  63.                 writer.newline();
  64.                 writer.flush();
  65.             }
  66.             system.out.println("登录成功,您的用户名为:"sender);
  67.             
  68.             //启动接收消息线程
  69.             new thread(new clientthread(socket)).start();
  70.             //连接管道
  71.             pipedreader=new pipedreader(clientthread.getpipedwriter());
  72.             /******************登录验证模块*************************************************************/
  73.             
  74.             /******************聊天模块************************/
  75.             
  76.             while(isloginout.equals("n")){
  77.                 
  78.                 /************************好友在线验证,在线则开始聊天,否则重新输入好友*****************************************/
  79.                 while(isfriendonline==false){
  80.                     system.out.print("请输入好友:");
  81.                     recerver=in.readline();
  82.                     //规范判断好友在线指令格式
  83.                     string instruction="/instruction"recerver"tmp";
  84.                     //发送指令
  85.                     writer.write(instruction);
  86.                     writer.newline();
  87.                     writer.flush();
  88.                     //通过管道接收后台线程的返回值
  89.                     char[] charreader=new char[1];
  90.                     pipedreader.read(charreader,0,1);
  91.                     
  92.                     msg=new string(charreader);
  93.                     //分解消息
  94.                     if(msg.equals("n")){
  95.                         isfriendonline=false;
  96.                         system.out.println("sorry,好友"recerver"现在不在线,请重新输入好友...");
  97.                     }else {
  98.                         isfriendonline=true;
  99.                     }
  100.                 }
  101.                 
  102.                 system.out.println("与"recerver"连接成功,开始聊天!");
  103.                 /************************好友在线验证,在线则开始聊天,否则重新输入好友*****************************************/
  104.     
  105.                 while(flag){
  106.                     
  107.                     system.out.print("向"recerver"发送消息:");
  108.                     msg=in.readline();
  109.                     
  110.                     if(msg.equalsignorecase("bye") || msg==null){
  111.                         //规范消息格式
  112.                         msg=sender""recerver""msg;
  113.                         writer.write(msg);
  114.                         writer.newline();
  115.                         writer.flush();
  116.                         
  117.                         //判断是否退出
  118.                         system.out.print("结束与"sender"的聊天?(y/n):");
  119.                         isloginout=in.readline();
  120.                         while(isloginout.equals("n")==false && isloginout.equals("y")==false){
  121.                             system.out.printf("您的输入错误,请重新输入\n结束与"sender"的聊天?(y/n):");
  122.                             isloginout=in.readline();
  123.                         }
  124.                         if(isloginout.equals("n")){
  125.                             flag=true;
  126.                         }else {
  127.                             system.out.println("已退出!!!");
  128.                             flag=false;
  129.                         }
  130.                         
  131.                     }else {
  132.                         //规范消息格式
  133.                         msg=sender""recerver""msg;
  134.                         
  135.                         writer.write(msg);
  136.                         writer.newline();
  137.                         writer.flush();
  138.                     }
  139.                 }
  140.             }
  141.             /******************聊天模块************************/
  142.         }catch(exception ex){
  143.             ex.printstacktrace();
  144.         }finally{
  145.             try {
  146.                 socket.close();
  147.                 pipedreader.close();
  148.             } catch (ioexception e) {
  149.                 // todo auto-generated catch block
  150.                 e.printstacktrace();
  151.             }
  152.         }
  153.     }
  154. }

clientthread.java

  1. package cn.com.xiebiao.smallqqclient;

  2. import java.io.bufferedreader;
  3. import java.io.ioexception;
  4. import java.io.inputstreamreader;
  5. import java.io.pipedwriter;
  6. import java.net.socket;

  7. /**
  8.  *
  9.  * title : clientthread.java 
  10.  * author : vibe xie @
  11.  * time : mar 22, 2015 3:28:08 pm
  12.  * 凯发app官方网站 copyright: 凯发app官方网站 copyright (c) 2015
  13.  * description:
  14.  */
  15. public class clientthread implements runnable{
  16.     private socket socket;
  17.     private static string sender;
  18.     private static string msg;
  19.     private static string message;
  20.     private static boolean running=true;
  21.     //通往qqclient的管道
  22.     private static pipedwriter pipedwriter=new pipedwriter();
  23.     public static pipedwriter getpipedwriter() {
  24.         return pipedwriter;
  25.     }
  26.     
  27.     public clientthread(socket socket) {
  28.         // todo auto-generated constructor stub
  29.         this.socket=socket;
  30.     }
  31.     
  32.     @override
  33.     public void run() {
  34.         // todo auto-generated method stub
  35.         try {
  36.             bufferedreader reader=new bufferedreader(new inputstreamreader(socket.getinputstream()));
  37.             while(running){
  38.                 msg=reader.readline();
  39.                 msganalyseutil msganalyseutil=new msganalyseutil(msg);
  40.                 sender=msganalyseutil.getsender();
  41.                 message=msganalyseutil.getmessage();
  42.                 
  43.                 //得到回复用户是否在线的指令,通过管道回写给qqclient
  44.                 if(sender.equals("/instruction")){
  45.                     if(message.equals("bye")){
  46.                         running=false;
  47.                     }else {
  48.                         pipedwriter.write(message);
  49.                     }
  50.                 }else {
  51.                     system.out.printf("\n来自"sender"的信息:"message"\n");
  52.                 }
  53.             }
  54.         } catch (ioexception e) {
  55.             // todo auto-generated catch block
  56.             e.printstacktrace();
  57.         }

  58.     }
  59. }
另外客户端还有一个文件msganalyseutil.java是服务器端是一样的。



阅读(1822) | 评论(0) | 转发(0) |
0

上一篇:

下一篇:

给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图