Java Socket聊天室编程(二)之利用socket实现单聊聊天室
这篇文章主要介绍了Java 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(); // 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 += " "; // 这里修改成向全部连接到服务器的用户推送消息 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 += " "; 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()); || status == ) { 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)) { ;//没有输入完整密码 } ")) { ;//用户1 } ")) { ;//用户2 } else { ;//密码错误 } } /** * 实例化一个聊天服务 * * @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 += " "; 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 Socket聊天室编程(二)之利用socket实现单聊聊天室的更多相关文章
- Java Socket聊天室编程(一)之利用socket实现聊天之消息推送
这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...
- Java高并发网络编程(二)BIO
一.阻塞 服务器端 public class BIOServer { public static void main(String[] args) throws Exception { ServerS ...
- 【JAVA】IOS内购二次验证及掉单问题解决
这个估计是我踩过的最大的坑,当时做微信支付的时候也没这么坑爹,当然他俩也半斤八两... 苹果官方明确表示:验证支付时,可能会有一定的延迟.第一次处理的时间就专注的解决这个问题了,忽略了掉单的问题(稍后 ...
- workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)
workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...
- 浅谈JAVA中如何利用socket进行网络编程(二)
转自:http://developer.51cto.com/art/201106/268386.htm Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以 ...
- Java多线程Socket在控制台输出的多人聊天室编程
服务器端代码 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java ...
- 浅谈JAVA中如何利用socket进行网络编程(一)
转自:http://developer.51cto.com/art/201106/268385.htm Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以 ...
- Java网络编程二:Socket详解
Socket又称套接字,是连接运行在网络上两个程序间的双向通讯的端点. 一.使用Socket进行网络通信的过程 服务端:服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户端的连接 ...
- 利用socket.io+nodejs打造简单聊天室
代码地址如下:http://www.demodashi.com/demo/11579.html 界面展示: 首先展示demo的结果界面,只是简单消息的发送和接收,包括发送文字和发送图片. ws说明: ...
随机推荐
- SVN使用教程2017.10.6
http://www.cnblogs.com/mq0036/p/5250198.html
- pymysql和 SQLAlchemy在python下的使用
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine, Table, Column, In ...
- 插入排序算法java
转自https://blog.csdn.net/jianyuerensheng/article/details/51254415 1.基本思想 直接插入排序的基本操作是将一个记录插入到已经排好的有序表 ...
- logrus_hook.go
package) //表示自身栈中跳过6个,:] entry.Data["file"] = fileName entry.Data["func" ...
- nsq源码阅读笔记之nsqd(四)——Channel
与Channel相关的代码主要位于nsqd/channel.go, nsqd/nsqd.go中. Channel与Topic的关系 Channel是消费者订阅特定Topic的一种抽象.对于发往Topi ...
- golang 1.8 优雅关闭
// main.go package main import ( "fmt" "log" "net/http" "os" ...
- BZOJ_1801_[Ahoi2009]chess 中国象棋_DP
BZOJ_1801_[Ahoi2009]chess 中国象棋_DP Description 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像 ...
- Selenium在定位的class含有空格的复合类的解决办法整理
1.class属性唯一但是有空格,选择空格两边唯一的哪一个 <div id="tempConfigTable" class="dtb-style-1 table-d ...
- Windows Ubuntu Bash申请免费通配符证书(Let's Encrypt)并绑定IIS
什么是 Let’s Encrypt? 部署 HTTPS 网站的时候需要证书,证书由 CA 机构签发,大部分传统 CA 机构签发证书是需要收费的,这不利于推动 HTTPS 协议的使用. Let’s En ...
- 将wiki人脸数据集中的图片按男女分类
import shutil f_in = 'D:/wiki_crop' # 读取文件中图片信息根据性别分类图片到对应目录中 dirroot = "D:/" f = open(dir ...