在上篇文章Java Socket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯。
其实就是建立一个一对一的聊天通讯。
与上一篇实现消息推送的代码有些不同,在它上面加以修改的。
如果没有提到的方法或者类则和上一篇一模一样。
1,修改实体类(服务器端和客户端的实体类是一样的)
1,UserInfoBean 用户信息表
public class UserInfoBean implements Serializable {private static final long serialVersionUID = 2L;private long userId;// 用户idprivate 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;// 消息idprivate long groupId;// 群idprivate boolean isGoup;// 是否是群消息private int chatType;// 消息类型;1,文本;2,图片;3,小视频;4,文件;5,地理位置;6,语音;7,视频通话private String content;// 文本消息内容private String errorMsg;// 错误信息private int errorCode;// 错误代码private int userId;//用户idprivate int friendId;//目标好友idprivate MessageFileBean chatFile;// 消息附件//省略get、set方法}
3,MessageFileBean 消息附件表
public class MessageFileBean implements Serializable {private static final long serialVersionUID = 3L;private int fileId;//文件idprivate 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存储所有的Socketpublic List<Socket> socketList = new ArrayList<>();// 模仿保存在内存中的socketpublic 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 blocke.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 blocke.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 blocke.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 blocke.printStackTrace();}}}}
3,(客户端代码)LoginActivity 登陆页面修改可以登录多人
public class LoginActivity extends AppCompatActivity {private EditText chat_name_text, chat_pwd_text;private Button chat_login_btn;@Overrideprotected 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() {@Overridepublic 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和聊天目标用户idif (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-8String 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() {@Overridepublic 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实现单聊聊天室,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对武林网网站的支持!
新闻热点
疑难解答