Java Socket聊天室编程(二)之利用socket实现单聊聊天室
在上篇文章JavaSocket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯。
其实就是建立一个一对一的聊天通讯。
与上一篇实现消息推送的代码有些不同,在它上面加以修改的。
如果没有提到的方法或者类则和上一篇一模一样。
1,修改实体类(服务器端和客户端的实体类是一样的)
1,UserInfoBean用户信息表
publicclassUserInfoBeanimplementsSerializable{ privatestaticfinallongserialVersionUID=2L; privatelonguserId;//用户id privateStringuserName;//用户名 privateStringlikeName;//昵称 privateStringuserPwd;//用户密码 privateStringuserIcon;//用户头像 //省略get、set方法 }
2,MessageBean聊天信息表
publicclassMessageBeanimplementsSerializable{ privatestaticfinallongserialVersionUID=1L; privatelongmessageId;//消息id privatelonggroupId;//群id privatebooleanisGoup;//是否是群消息 privateintchatType;//消息类型;1,文本;2,图片;3,小视频;4,文件;5,地理位置;6,语音;7,视频通话 privateStringcontent;//文本消息内容 privateStringerrorMsg;//错误信息 privateinterrorCode;//错误代码 privateintuserId;//用户id privateintfriendId;//目标好友id privateMessageFileBeanchatFile;//消息附件 //省略get、set方法 }
3,MessageFileBean消息附件表
publicclassMessageFileBeanimplementsSerializable{ privatestaticfinallongserialVersionUID=3L; privateintfileId;//文件id privateStringfileName;//文件名称 privatelongfileLength;//文件长度 privateByte[]fileByte;//文件内容 privateStringfileType;//文件类型 privateStringfileTitle;//文件头名称 //省略get、set方法 }
2,(服务器端代码修改)ChatServer主要的聊天服务类,加以修改
publicclassChatServer{ //socket服务 privatestaticServerSocketserver; //使用ArrayList存储所有的Socket publicList<Socket>socketList=newArrayList<>(); //模仿保存在内存中的socket publicMap<Integer,Socket>socketMap=newHashMap(); //模仿保存在数据库中的用户信息 publicMap<Integer,UserInfoBean>userMap=newHashMap(); publicGsongson=newGson(); /** *初始化socket服务 */ publicvoidinitServer(){ try{ //创建一个ServerSocket在端口8080监听客户请求 server=newServerSocket(SocketUrls.PORT); createMessage(); }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } /** *创建消息管理,一直接收消息 */ privatevoidcreateMessage(){ try{ System.out.println("等待用户接入:"); //使用accept()阻塞等待客户请求 Socketsocket=server.accept(); //将链接进来的socket保存到集合中 socketList.add(socket); System.out.println("用户接入:"+socket.getPort()); //开启一个子线程来等待另外的socket加入 newThread(newRunnable(){ publicvoidrun(){ //再次创建一个socket服务等待其他用户接入 createMessage(); } }).start(); //用于服务器推送消息给用户 getMessage(); //从客户端获取信息 BufferedReaderbff=newBufferedReader(newInputStreamReader(socket.getInputStream())); //读取发来服务器信息 Stringline=null; //循环一直接收当前socket发来的消息 while(true){ Thread.sleep(500); //System.out.println("内容:"+bff.readLine()); //获取客户端的信息 while((line=bff.readLine())!=null){ //解析实体类 MessageBeanmessageBean=gson.fromJson(line,MessageBean.class); //将用户信息添加进入map中,模仿添加进数据库和内存 //实体类存入数据库,socket存入内存中,都以用户id作为参照 setChatMap(messageBean,socket); //将用户发送进来的消息转发给目标好友 getFriend(messageBean); System.out.println("用户:"+userMap.get(messageBean.getUserId()).getUserName()); System.out.println("内容:"+messageBean.getContent()); } } //server.close(); }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); System.out.println("错误:"+e.getMessage()); } } /** *发送消息 */ privatevoidgetMessage(){ newThread(newRunnable(){ publicvoidrun(){ try{ Stringbuffer; while(true){ //从控制台输入 BufferedReaderstrin=newBufferedReader(newInputStreamReader(System.in)); buffer=strin.readLine(); //因为readLine以换行符为结束点所以,结尾加入换行 buffer+="\n"; //这里修改成向全部连接到服务器的用户推送消息 for(Socketsocket:socketMap.values()){ OutputStreamoutput=socket.getOutputStream(); output.write(buffer.getBytes("utf-8")); //发送数据 output.flush(); } } }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } }).start(); } /** *模拟添加信息进入数据库和内存 * *@parammessageBean *@paramscoket */ privatevoidsetChatMap(MessageBeanmessageBean,Socketscoket){ //将用户信息存起来 if(userMap!=null&&userMap.get(messageBean.getUserId())==null){ userMap.put(messageBean.getUserId(),getUserInfoBean(messageBean.getUserId())); } //将对应的链接进来的socket存起来 if(socketMap!=null&&socketMap.get(messageBean.getUserId())==null){ socketMap.put(messageBean.getUserId(),scoket); } } /** *模拟数据库的用户信息,这里创建id不同的用户信息 * *@paramuserId *@return */ privateUserInfoBeangetUserInfoBean(intuserId){ UserInfoBeanuserInfoBean=newUserInfoBean(); userInfoBean.setUserIcon("用户头像"); userInfoBean.setUserId(userId); userInfoBean.setUserName("admin"); userInfoBean.setUserPwd("123123132a"); returnuserInfoBean; } /** *将消息转发给目标好友 * *@parammessageBean */ privatevoidgetFriend(MessageBeanmessageBean){ if(socketMap!=null&&socketMap.get(messageBean.getFriendId())!=null){ Socketsocket=socketMap.get(messageBean.getFriendId()); Stringbuffer=gson.toJson(messageBean); //因为readLine以换行符为结束点所以,结尾加入换行 buffer+="\n"; try{ //向客户端发送信息 OutputStreamoutput=socket.getOutputStream(); output.write(buffer.getBytes("utf-8")); //发送数据 output.flush(); }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } } }
3,(客户端代码)LoginActivity登陆页面修改可以登录多人
publicclassLoginActivityextendsAppCompatActivity{ privateEditTextchat_name_text,chat_pwd_text; privateButtonchat_login_btn; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); chat_name_text=(EditText)findViewById(R.id.chat_name_text); chat_pwd_text=(EditText)findViewById(R.id.chat_pwd_text); chat_login_btn=(Button)findViewById(R.id.chat_login_btn); chat_login_btn.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ intstatus=getLogin(chat_name_text.getText().toString().trim(),chat_pwd_text.getText().toString().trim()); if(status==-1||status==0){ Toast.makeText(LoginActivity.this,"密码错误",Toast.LENGTH_LONG).show(); return; } getChatServer(getLogin(chat_name_text.getText().toString().trim(),chat_pwd_text.getText().toString().trim())); Intentintent=newIntent(LoginActivity.this,MainActivity.class); startActivity(intent); finish(); } }); } /** *返回登陆状态,1为用户,2为另一个用户,这里模拟出两个用户互相通讯 * *@paramname *@parampwd *@return */ privateintgetLogin(Stringname,Stringpwd){ if(TextUtils.isEmpty(name)||TextUtils.isEmpty(pwd)){ return0;//没有输入完整密码 }elseif(name.equals("admin")&&pwd.equals("1")){ return1;//用户1 }elseif(name.equals("admin")&&pwd.equals("2")){ return2;//用户2 }else{ return-1;//密码错误 } } /** *实例化一个聊天服务 * *@paramstatus */ privatevoidgetChatServer(intstatus){ ChatAppliaction.chatServer=newChatServer(status); } }
4,(客户端代码)ChatServer聊天服务代码逻辑的修改
publicclassChatServer{ privateSocketsocket; privateHandlerhandler; privateMessageBeanmessageBean; privateGsongson=newGson(); //由Socket对象得到输出流,并构造PrintWriter对象 PrintWriterprintWriter; InputStreaminput; OutputStreamoutput; DataOutputStreamdataOutputStream; publicChatServer(intstatus){ initMessage(status); initChatServer(); } /** *消息队列,用于传递消息 * *@paramhandler */ publicvoidsetChatHandler(Handlerhandler){ this.handler=handler; } privatevoidinitChatServer(){ //开个线程接收消息 receiveMessage(); } /** *初始化用户信息 */ privatevoidinitMessage(intstatus){ messageBean=newMessageBean(); UserInfoBeanuserInfoBean=newUserInfoBean(); userInfoBean.setUserId(2); messageBean.setMessageId(1); messageBean.setChatType(1); userInfoBean.setUserName("admin"); userInfoBean.setUserPwd("123123123a"); //以下操作模仿当用户点击了某个好友展开的聊天界面,将保存用户id和聊天目标用户id if(status==1){//如果是用户1,那么他就指向用户2聊天 messageBean.setUserId(1); messageBean.setFriendId(2); }elseif(status==2){//如果是用户2,那么他就指向用户1聊天 messageBean.setUserId(2); messageBean.setFriendId(1); } ChatAppliaction.userInfoBean=userInfoBean; } /** *发送消息 * *@paramcontentMsg */ publicvoidsendMessage(StringcontentMsg){ try{ if(socket==null){ Messagemessage=handler.obtainMessage(); message.what=1; message.obj="服务器已经关闭"; handler.sendMessage(message); return; } byte[]str=contentMsg.getBytes("utf-8");//将内容转utf-8 Stringaaa=newString(str); messageBean.setContent(aaa); StringmessageJson=gson.toJson(messageBean); /** *因为服务器那边的readLine()为阻塞读取 *如果它读取不到换行符或者输出流结束就会一直阻塞在那里 *所以在json消息最后加上换行符,用于告诉服务器,消息已经发送完毕了 **/ messageJson+="\n"; output.write(messageJson.getBytes("utf-8"));//换行打印 output.flush();//刷新输出流,使Server马上收到该字符串 }catch(Exceptione){ e.printStackTrace(); Log.e("test","错误:"+e.toString()); } } /** *接收消息,在子线程中 */ privatevoidreceiveMessage(){ newThread(newRunnable(){ @Override publicvoidrun(){ try{ //向本机的8080端口发出客户请求 socket=newSocket(SocketUrls.IP,SocketUrls.PORT); //由Socket对象得到输入流,并构造相应的BufferedReader对象 printWriter=newPrintWriter(socket.getOutputStream()); input=socket.getInputStream(); output=socket.getOutputStream(); dataOutputStream=newDataOutputStream(socket.getOutputStream()); //从客户端获取信息 BufferedReaderbff=newBufferedReader(newInputStreamReader(input)); //读取发来服务器信息 Stringline; while(true){ Thread.sleep(500); //获取客户端的信息 while((line=bff.readLine())!=null){ Log.i("socket","内容:"+line); MessageBeanmessageBean=gson.fromJson(line,MessageBean.class); Messagemessage=handler.obtainMessage(); message.obj=messageBean.getContent(); message.what=1; handler.sendMessage(message); } if(socket==null) break; } output.close();//关闭Socket输出流 input.close();//关闭Socket输入流 socket.close();//关闭Socket }catch(Exceptione){ e.printStackTrace(); Log.e("test","错误:"+e.toString()); } } }).start(); } publicSocketgetSocekt(){ if(socket==null)returnnull; returnsocket; } }
如此一来,代码逻辑已经从消息推送的逻辑修改成了单聊的逻辑了。
这个代码可以让用户1和用户2相互聊天,并且服务器会记录下他们之间的聊天记录。并且服务器还是拥有消息推送的功能。
以上所述是小编给大家介绍的JavaSocket聊天室编程(二)之利用socket实现单聊聊天室,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!