这篇文章主要介绍了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实现单聊聊天室的更多相关文章

  1. Java Socket聊天室编程(一)之利用socket实现聊天之消息推送

    这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...

  2. Java高并发网络编程(二)BIO

    一.阻塞 服务器端 public class BIOServer { public static void main(String[] args) throws Exception { ServerS ...

  3. 【JAVA】IOS内购二次验证及掉单问题解决

    这个估计是我踩过的最大的坑,当时做微信支付的时候也没这么坑爹,当然他俩也半斤八两... 苹果官方明确表示:验证支付时,可能会有一定的延迟.第一次处理的时间就专注的解决这个问题了,忽略了掉单的问题(稍后 ...

  4. workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)

    workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...

  5. 浅谈JAVA中如何利用socket进行网络编程(二)

    转自:http://developer.51cto.com/art/201106/268386.htm Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以 ...

  6. Java多线程Socket在控制台输出的多人聊天室编程

    服务器端代码 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java ...

  7. 浅谈JAVA中如何利用socket进行网络编程(一)

    转自:http://developer.51cto.com/art/201106/268385.htm Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以 ...

  8. Java网络编程二:Socket详解

    Socket又称套接字,是连接运行在网络上两个程序间的双向通讯的端点. 一.使用Socket进行网络通信的过程 服务端:服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户端的连接 ...

  9. 利用socket.io+nodejs打造简单聊天室

    代码地址如下:http://www.demodashi.com/demo/11579.html 界面展示: 首先展示demo的结果界面,只是简单消息的发送和接收,包括发送文字和发送图片. ws说明: ...

随机推荐

  1. 数据库历险记(一) | MySQL这么好,为什么还有人用Oracle?

    关系型数据库(Relational DataBase Management System),简称 RDBMS.说起关系型数据库,我们脑海中会立即浮现出 Oracle.MySQL.SQLServer 等 ...

  2. jQuery学习之旅 Item1 选择器【一】

    点击"名称"会跳转到此方法的jQuery官方说明文档. 1. 基础选择器 Basics 名称 说明 举例 #id 根据元素Id选择 $("divId") 选择I ...

  3. java 泛型详解(普通泛型、 通配符、 泛型接口,泛型数组,泛型方法,泛型嵌套)

    JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型.  1.Java泛型  其实Java ...

  4. TestNG详解-深度好文

    转自: https://blog.csdn.net/lykangjia/article/details/56485295 TestNG详解-深度好文 2017年02月22日 14:51:52 阅读数: ...

  5. Smokeping

    Smokeping允许你监测多台服务器. Smokeping使用RRDtool来存储数据,另外,其可基于RRDtool输出生成相应的统计图表. Smokeping由两个部分组成.一个运行在后台.定期收 ...

  6. LNMP单点服务器搭建

    一.部署服务器环境 Linux:centos6.5 nginx:1.14.0 mysql:5.6.33 php:5.6.36 1.网络配置 2.FQDN /etc/hosts /etc/sysconf ...

  7. 关于SELinux

    出现背景以及发展历程 SELinux是「Security-Enhanced Linux」的简称,是美国国家安全局「NSA=The National Security Agency」 和SCC(Secu ...

  8. 在C++中怎么判断一个double型数据的小数点部分是否为零

    例:double sf = 123.123: 这里我们怎么判断sf小数点部分是否为零,可以直接用原数减去将sf强制转换后的整数是否为零来判断. if((sf - (int)sf) == 0),则说明s ...

  9. HEOI2018 游记

    day0早上没有跑操,收拾了点东西,带了点吃的,老妈打电话说要给送点厚衣服,好像确实有点冷.上午有考试,说自愿考,然后开到一半就没了,根本没人打啊,打了一道小园丁和老司机,一个一个部分分打,最后T了一 ...

  10. java线程同步小结

    1.线程同步的目的是为了防止多个线程同时访问一个资源时对资源的破坏 2.线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无 ...