服务器之家

服务器之家 > 正文

Java Socket聊天室编程(二)之利用socket实现单聊聊天室

时间:2020-06-16 11:26     来源/作者:夜辉疾风

在上篇文章Java Socket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯。

其实就是建立一个一对一的聊天通讯。

与上一篇实现消息推送的代码有些不同,在它上面加以修改的。

如果没有提到的方法或者类则和上一篇一模一样。

1,修改实体类(服务器端和客户端的实体类是一样的)

1,UserInfoBean 用户信息表

?
1
2
3
4
5
6
7
8
9
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 聊天信息表

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 消息附件表

?
1
2
3
4
5
6
7
8
9
10
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 主要的聊天服务类,加以修改

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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 登陆页面修改可以登录多人

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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 聊天服务代码逻辑的修改

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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实现单聊聊天室,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!

原文链接:http://blog.csdn.net/yehui928186846/article/details/52583142

标签:

相关文章

热门资讯

歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意 2019-07-07
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
返回顶部