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

网友投稿 240 2023-07-06


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 socketList = new ArrayList<>();

// 模仿保存在内存中的socket

public Map socketMap = new HashMap();

// 模仿保存在数据库中的用户信息

public Map 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.pcMxhXIeRrintln("用户 : " + 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实现单聊聊天室,希望对大家有所帮助,如果大家有任何疑问请给我留言,会及时回复大家的。在此也非常感谢大家对我们网站的支持!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:BootStrap入门教程(三)之响应式原理
下一篇:BootStrap入门教程(二)之固定的内置样式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~