Java Socket聊天室编程(二)之利用socket实现单聊聊天室
程序员文章站
2024-03-13 15:08:09
在上篇文章java socket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我...
在上篇文章java socket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯。
其实就是建立一个一对一的聊天通讯。
与上一篇实现消息推送的代码有些不同,在它上面加以修改的。
如果没有提到的方法或者类则和上一篇一模一样。
1,修改实体类(服务器端和客户端的实体类是一样的)
1,userinfobean 用户信息表
public class userinfobean implements serializable { private static final long serialversionuid = 2l; private long userid;// 用户id private string username;// 用户名 private string likename;// 昵称 private string userpwd;// 用户密码 private string usericon;// 用户头像 //省略get、set方法 }
2,messagebean 聊天信息表
public class messagebean implements serializable { private static final long serialversionuid = 1l; private long messageid;// 消息id private long groupid;// 群id private boolean isgoup;// 是否是群消息 private int chattype;// 消息类型;1,文本;2,图片;3,小视频;4,文件;5,地理位置;6,语音;7,视频通话 private string content;// 文本消息内容 private string errormsg;// 错误信息 private int errorcode;// 错误代码 private int userid;//用户id private int friendid;//目标好友id private messagefilebean chatfile;// 消息附件 //省略get、set方法 }
3,messagefilebean 消息附件表
public class messagefilebean implements serializable { private static final long serialversionuid = 3l; private int fileid;//文件id private string filename;//文件名称 private long filelength;//文件长度 private byte[] filebyte;//文件内容 private string filetype;//文件类型 private string filetitle;//文件头名称 //省略get、set方法 }
2,(服务器端代码修改)chatserver 主要的聊天服务类,加以修改
public class chatserver { // socket服务 private static serversocket server; // 使用arraylist存储所有的socket public list<socket> socketlist = new arraylist<>(); // 模仿保存在内存中的socket public map<integer, socket> socketmap = new hashmap(); // 模仿保存在数据库中的用户信息 public map<integer, userinfobean> usermap = new hashmap(); public gson gson = new gson(); /** * 初始化socket服务 */ public void initserver() { try { // 创建一个serversocket在端口8080监听客户请求 server = new serversocket(socketurls.port); createmessage(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } /** * 创建消息管理,一直接收消息 */ private void createmessage() { try { system.out.println("等待用户接入 : "); // 使用accept()阻塞等待客户请求 socket socket = server.accept(); // 将链接进来的socket保存到集合中 socketlist.add(socket); system.out.println("用户接入 : " + socket.getport()); // 开启一个子线程来等待另外的socket加入 new thread(new runnable() { public void run() { // 再次创建一个socket服务等待其他用户接入 createmessage(); } }).start(); // 用于服务器推送消息给用户 getmessage(); // 从客户端获取信息 bufferedreader bff = new bufferedreader(new inputstreamreader(socket.getinputstream())); // 读取发来服务器信息 string line = null; // 循环一直接收当前socket发来的消息 while (true) { thread.sleep(500); // system.out.println("内容 : " + bff.readline()); // 获取客户端的信息 while ((line = bff.readline()) != null) { // 解析实体类 messagebean messagebean = 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 (exception e) { // todo auto-generated catch block e.printstacktrace(); system.out.println("错误 : " + e.getmessage()); } } /** * 发送消息 */ private void getmessage() { new thread(new runnable() { public void run() { try { string buffer; while (true) { // 从控制台输入 bufferedreader strin = new bufferedreader(new inputstreamreader(system.in)); buffer = strin.readline(); // 因为readline以换行符为结束点所以,结尾加入换行 buffer += "\n"; // 这里修改成向全部连接到服务器的用户推送消息 for (socket socket : socketmap.values()) { outputstream output = socket.getoutputstream(); output.write(buffer.getbytes("utf-8")); // 发送数据 output.flush(); } } } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } }).start(); } /** * 模拟添加信息进入数据库和内存 * * @param messagebean * @param scoket */ private void setchatmap(messagebean messagebean, socket scoket) { // 将用户信息存起来 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不同的用户信息 * * @param userid * @return */ private userinfobean getuserinfobean(int userid) { userinfobean userinfobean = new userinfobean(); userinfobean.setusericon("用户头像"); userinfobean.setuserid(userid); userinfobean.setusername("admin"); userinfobean.setuserpwd("123123132a"); return userinfobean; } /** * 将消息转发给目标好友 * * @param messagebean */ private void getfriend(messagebean messagebean) { if (socketmap != null && socketmap.get(messagebean.getfriendid()) != null) { socket socket = socketmap.get(messagebean.getfriendid()); string buffer = gson.tojson(messagebean); // 因为readline以换行符为结束点所以,结尾加入换行 buffer += "\n"; try { // 向客户端发送信息 outputstream output = socket.getoutputstream(); output.write(buffer.getbytes("utf-8")); // 发送数据 output.flush(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } } }
3,(客户端代码)loginactivity 登陆页面修改可以登录多人
public class loginactivity extends appcompatactivity { private edittext chat_name_text, chat_pwd_text; private button chat_login_btn; @override protected void oncreate(bundle savedinstancestate) { 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(new view.onclicklistener() { @override public void onclick(view v) { int status = 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())); intent intent = new intent(loginactivity.this, mainactivity.class); startactivity(intent); finish(); } }); } /** * 返回登陆状态,1为用户,2为另一个用户,这里模拟出两个用户互相通讯 * * @param name * @param pwd * @return */ private int getlogin(string name, string pwd) { if (textutils.isempty(name) || textutils.isempty(pwd)) { return 0;//没有输入完整密码 } else if (name.equals("admin") && pwd.equals("1")) { return 1;//用户1 } else if (name.equals("admin") && pwd.equals("2")) { return 2;//用户2 } else { return -1;//密码错误 } } /** * 实例化一个聊天服务 * * @param status */ private void getchatserver(int status) { chatappliaction.chatserver = new chatserver(status); } }
4,(客户端代码)chatserver 聊天服务代码逻辑的修改
public class chatserver { private socket socket; private handler handler; private messagebean messagebean; private gson gson = new gson(); // 由socket对象得到输出流,并构造printwriter对象 printwriter printwriter; inputstream input; outputstream output; dataoutputstream dataoutputstream; public chatserver(int status) { initmessage(status); initchatserver(); } /** * 消息队列,用于传递消息 * * @param handler */ public void setchathandler(handler handler) { this.handler = handler; } private void initchatserver() { //开个线程接收消息 receivemessage(); } /** * 初始化用户信息 */ private void initmessage(int status) { messagebean = new messagebean(); userinfobean userinfobean = new userinfobean(); 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); } else if (status == 2) {//如果是用户2,那么他就指向用户1聊天 messagebean.setuserid(2); messagebean.setfriendid(1); } chatappliaction.userinfobean = userinfobean; } /** * 发送消息 * * @param contentmsg */ public void sendmessage(string contentmsg) { try { if (socket == null) { message message = handler.obtainmessage(); message.what = 1; message.obj = "服务器已经关闭"; handler.sendmessage(message); return; } byte[] str = contentmsg.getbytes("utf-8");//将内容转utf-8 string aaa = new string(str); messagebean.setcontent(aaa); string messagejson = gson.tojson(messagebean); /** * 因为服务器那边的readline()为阻塞读取 * 如果它读取不到换行符或者输出流结束就会一直阻塞在那里 * 所以在json消息最后加上换行符,用于告诉服务器,消息已经发送完毕了 * */ messagejson += "\n"; output.write(messagejson.getbytes("utf-8"));// 换行打印 output.flush(); // 刷新输出流,使server马上收到该字符串 } catch (exception e) { e.printstacktrace(); log.e("test", "错误:" + e.tostring()); } } /** * 接收消息,在子线程中 */ private void receivemessage() { new thread(new runnable() { @override public void run() { try { // 向本机的8080端口发出客户请求 socket = new socket(socketurls.ip, socketurls.port); // 由socket对象得到输入流,并构造相应的bufferedreader对象 printwriter = new printwriter(socket.getoutputstream()); input = socket.getinputstream(); output = socket.getoutputstream(); dataoutputstream = new dataoutputstream(socket.getoutputstream()); // 从客户端获取信息 bufferedreader bff = new bufferedreader(new inputstreamreader(input)); // 读取发来服务器信息 string line; while (true) { thread.sleep(500); // 获取客户端的信息 while ((line = bff.readline()) != null) { log.i("socket", "内容 : " + line); messagebean messagebean = gson.fromjson(line, messagebean.class); message message = 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 (exception e) { e.printstacktrace(); log.e("test", "错误:" + e.tostring()); } } }).start(); } public socket getsocekt() { if (socket == null) return null; return socket; } }
如此一来,代码逻辑已经从消息推送的逻辑修改成了单聊的逻辑了。
这个代码可以让用户1和用户2相互聊天,并且服务器会记录下他们之间的聊天记录。并且服务器还是拥有消息推送的功能。
以上所述是小编给大家介绍的java socket聊天室编程(二)之利用socket实现单聊聊天室,希望对大家有所帮助
下一篇: Java关于IO流的全面介绍