在上篇文章Java Socket聊天室編程(一)之利用socket實(shí)現(xiàn)聊天之消息推送中我們講到如何使用socket讓服務(wù)器和客戶(hù)端之間傳遞消息,達(dá)到推送消息的目的,接下來(lái)我將寫(xiě)出如何讓服務(wù)器建立客戶(hù)端與客戶(hù)端之間的通訊。
其實(shí)就是建立一個(gè)一對(duì)一的聊天通訊。
與上一篇實(shí)現(xiàn)消息推送的代碼有些不同,在它上面加以修改的。
如果沒(méi)有提到的方法或者類(lèi)則和上一篇一模一樣。
1,修改實(shí)體類(lèi)(服務(wù)器端和客戶(hù)端的實(shí)體類(lèi)是一樣的)
1,UserInfoBean 用戶(hù)信息表
public class UserInfoBean implements Serializable {private static final long serialVersionUID = 2L;private long userId;// 用戶(hù)idprivate String userName;// 用戶(hù)名private String likeName;// 昵稱(chēng)private String userPwd;// 用戶(hù)密碼private String userIcon;// 用戶(hù)頭像//省略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;// 消息類(lèi)型;1,文本;2,圖片;3,小視頻;4,文件;5,地理位置;6,語(yǔ)音;7,視頻通話(huà)private String content;// 文本消息內(nèi)容private String errorMsg;// 錯(cuò)誤信息private int errorCode;// 錯(cuò)誤代碼private int userId;//用戶(hù)idprivate int friendId;//目標(biāo)好友idprivate MessageFileBean chatFile;// 消息附件//省略get、set方法}3,MessageFileBean 消息附件表
public class MessageFileBean implements Serializable {private static final long serialVersionUID = 3L;private int fileId;//文件idprivate String fileName;//文件名稱(chēng)private long fileLength;//文件長(zhǎng)度private Byte[] fileByte;//文件內(nèi)容private String fileType;//文件類(lèi)型private String fileTitle;//文件頭名稱(chēng)//省略get、set方法}2,(服務(wù)器端代碼修改)ChatServer 主要的聊天服務(wù)類(lèi),加以修改
public class ChatServer {// socket服務(wù)private static ServerSocket server;// 使用ArrayList存儲(chǔ)所有的Socketpublic List<Socket> socketList = new ArrayList<>();// 模仿保存在內(nèi)存中的socketpublic Map<Integer, Socket> socketMap = new HashMap();// 模仿保存在數(shù)據(jù)庫(kù)中的用戶(hù)信息public Map<Integer, UserInfoBean> userMap = new HashMap();public Gson gson = new Gson();/*** 初始化socket服務(wù)*/public void initServer() {try {// 創(chuàng)建一個(gè)ServerSocket在端口8080監(jiān)聽(tīng)客戶(hù)請(qǐng)求server = new ServerSocket(SocketUrls.PORT);createMessage();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}/*** 創(chuàng)建消息管理,一直接收消息*/private void createMessage() {try {System.out.println("等待用戶(hù)接入 : ");// 使用accept()阻塞等待客戶(hù)請(qǐng)求Socket socket = server.accept();// 將鏈接進(jìn)來(lái)的socket保存到集合中socketList.add(socket);System.out.println("用戶(hù)接入 : " + socket.getPort());// 開(kāi)啟一個(gè)子線程來(lái)等待另外的socket加入new Thread(new Runnable() {public void run() {// 再次創(chuàng)建一個(gè)socket服務(wù)等待其他用戶(hù)接入createMessage();}}).start();// 用于服務(wù)器推送消息給用戶(hù)getMessage();// 從客戶(hù)端獲取信息BufferedReader bff = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 讀取發(fā)來(lái)服務(wù)器信息String line = null;// 循環(huán)一直接收當(dāng)前socket發(fā)來(lái)的消息while (true) {Thread.sleep(500);// System.out.println("內(nèi)容 : " + bff.readLine());// 獲取客戶(hù)端的信息while ((line = bff.readLine()) != null) {// 解析實(shí)體類(lèi)MessageBean messageBean = gson.fromJson(line, MessageBean.class);// 將用戶(hù)信息添加進(jìn)入map中,模仿添加進(jìn)數(shù)據(jù)庫(kù)和內(nèi)存// 實(shí)體類(lèi)存入數(shù)據(jù)庫(kù),socket存入內(nèi)存中,都以用戶(hù)id作為參照setChatMap(messageBean, socket);// 將用戶(hù)發(fā)送進(jìn)來(lái)的消息轉(zhuǎn)發(fā)給目標(biāo)好友getFriend(messageBean);System.out.println("用戶(hù) : " + userMap.get(messageBean.getUserId()).getUserName());System.out.println("內(nèi)容 : " + messageBean.getContent());}}// server.close();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();System.out.println("錯(cuò)誤 : " + e.getMessage());}}/*** 發(fā)送消息*/private void getMessage() {new Thread(new Runnable() {public void run() {try {String buffer;while (true) {// 從控制臺(tái)輸入BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));buffer = strin.readLine();// 因?yàn)閞eadLine以換行符為結(jié)束點(diǎn)所以,結(jié)尾加入換行buffer += "/n";// 這里修改成向全部連接到服務(wù)器的用戶(hù)推送消息for (Socket socket : socketMap.values()) {OutputStream output = socket.getOutputStream();output.write(buffer.getBytes("utf-8"));// 發(fā)送數(shù)據(jù)output.flush();}}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}).start();}/*** 模擬添加信息進(jìn)入數(shù)據(jù)庫(kù)和內(nèi)存* * @param messageBean* @param scoket*/private void setChatMap(MessageBean messageBean, Socket scoket) {// 將用戶(hù)信息存起來(lái)if (userMap != null && userMap.get(messageBean.getUserId()) == null) {userMap.put(messageBean.getUserId(), getUserInfoBean(messageBean.getUserId()));}// 將對(duì)應(yīng)的鏈接進(jìn)來(lái)的socket存起來(lái)if (socketMap != null && socketMap.get(messageBean.getUserId()) == null) {socketMap.put(messageBean.getUserId(), scoket);}}/*** 模擬數(shù)據(jù)庫(kù)的用戶(hù)信息,這里創(chuàng)建id不同的用戶(hù)信息* * @param userId* @return*/private UserInfoBean getUserInfoBean(int userId) {UserInfoBean userInfoBean = new UserInfoBean();userInfoBean.setUserIcon("用戶(hù)頭像");userInfoBean.setUserId(userId);userInfoBean.setUserName("admin");userInfoBean.setUserPwd("123123132a");return userInfoBean;}/*** 將消息轉(zhuǎn)發(fā)給目標(biāo)好友* * @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);// 因?yàn)閞eadLine以換行符為結(jié)束點(diǎn)所以,結(jié)尾加入換行buffer += "/n";try {// 向客戶(hù)端發(fā)送信息OutputStream output = socket.getOutputStream();output.write(buffer.getBytes("utf-8"));// 發(fā)送數(shù)據(jù)output.flush();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}3,(客戶(hù)端代碼)LoginActivity 登陸頁(yè)面修改可以登錄多人
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, "密碼錯(cuò)誤", 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();}});}/*** 返回登陸狀態(tài),1為用戶(hù),2為另一個(gè)用戶(hù),這里模擬出兩個(gè)用戶(hù)互相通訊** @param name* @param pwd* @return*/private int getLogin(String name, String pwd) {if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd)) {return 0;//沒(méi)有輸入完整密碼} else if (name.equals("admin") && pwd.equals("1")) {return 1;//用戶(hù)1} else if (name.equals("admin") && pwd.equals("2")) {return 2;//用戶(hù)2} else {return -1;//密碼錯(cuò)誤}}/*** 實(shí)例化一個(gè)聊天服務(wù)** @param status*/private void getChatServer(int status) {ChatAppliaction.chatServer = new ChatServer(status);}}4,(客戶(hù)端代碼)ChatServer 聊天服務(wù)代碼邏輯的修改
public class ChatServer {private Socket socket;private Handler handler;private MessageBean messageBean;private Gson gson = new Gson();// 由Socket對(duì)象得到輸出流,并構(gòu)造PrintWriter對(duì)象PrintWriter printWriter;InputStream input;OutputStream output;DataOutputStream dataOutputStream;public ChatServer(int status) {initMessage(status);initChatServer();}/*** 消息隊(duì)列,用于傳遞消息** @param handler*/public void setChatHandler(Handler handler) {this.handler = handler;}private void initChatServer() {//開(kāi)個(gè)線程接收消息receiveMessage();}/*** 初始化用戶(hù)信息*/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");//以下操作模仿當(dāng)用戶(hù)點(diǎn)擊了某個(gè)好友展開(kāi)的聊天界面,將保存用戶(hù)id和聊天目標(biāo)用戶(hù)idif (status == 1) {//如果是用戶(hù)1,那么他就指向用戶(hù)2聊天messageBean.setUserId(1);messageBean.setFriendId(2);} else if (status == 2) {//如果是用戶(hù)2,那么他就指向用戶(hù)1聊天messageBean.setUserId(2);messageBean.setFriendId(1);}ChatAppliaction.userInfoBean = userInfoBean;}/*** 發(fā)送消息** @param contentMsg*/public void sendMessage(String contentMsg) {try {if (socket == null) {Message message = handler.obtainMessage();message.what = 1;message.obj = "服務(wù)器已經(jīng)關(guān)閉";handler.sendMessage(message);return;}byte[] str = contentMsg.getBytes("utf-8");//將內(nèi)容轉(zhuǎn)utf-8String aaa = new String(str);messageBean.setContent(aaa);String messageJson = gson.toJson(messageBean);/*** 因?yàn)榉?wù)器那邊的readLine()為阻塞讀取* 如果它讀取不到換行符或者輸出流結(jié)束就會(huì)一直阻塞在那里* 所以在json消息最后加上換行符,用于告訴服務(wù)器,消息已經(jīng)發(fā)送完畢了* */messageJson += "/n";output.write(messageJson.getBytes("utf-8"));// 換行打印output.flush(); // 刷新輸出流,使Server馬上收到該字符串} catch (Exception e) {e.printStackTrace();Log.e("test", "錯(cuò)誤:" + e.toString());}}/*** 接收消息,在子線程中*/private void receiveMessage() {new Thread(new Runnable() {@Overridepublic void run() {try {// 向本機(jī)的8080端口發(fā)出客戶(hù)請(qǐng)求socket = new Socket(SocketUrls.IP, SocketUrls.PORT);// 由Socket對(duì)象得到輸入流,并構(gòu)造相應(yīng)的BufferedReader對(duì)象printWriter = new PrintWriter(socket.getOutputStream());input = socket.getInputStream();output = socket.getOutputStream();dataOutputStream = new DataOutputStream(socket.getOutputStream());// 從客戶(hù)端獲取信息BufferedReader bff = new BufferedReader(new InputStreamReader(input));// 讀取發(fā)來(lái)服務(wù)器信息String line;while (true) {Thread.sleep(500);// 獲取客戶(hù)端的信息while ((line = bff.readLine()) != null) {Log.i("socket", "內(nèi)容 : " + 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();//關(guān)閉Socket輸出流input.close();//關(guān)閉Socket輸入流socket.close();//關(guān)閉Socket} catch (Exception e) {e.printStackTrace();Log.e("test", "錯(cuò)誤:" + e.toString());}}}).start();}public Socket getSocekt() {if (socket == null) return null;return socket;}}如此一來(lái),代碼邏輯已經(jīng)從消息推送的邏輯修改成了單聊的邏輯了。
這個(gè)代碼可以讓用戶(hù)1和用戶(hù)2相互聊天,并且服務(wù)器會(huì)記錄下他們之間的聊天記錄。并且服務(wù)器還是擁有消息推送的功能。
以上所述是小編給大家介紹的Java Socket聊天室編程(二)之利用socket實(shí)現(xiàn)單聊聊天室,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)武林網(wǎng)網(wǎng)站的支持!
新聞熱點(diǎn)
疑難解答
圖片精選