在上篇文章Java Socket聊天室編程(一)之利用socket實(shí)現(xiàn)聊天之消息推送中我們講到如何使用socket讓服務(wù)器和客戶端之間傳遞消息,達(dá)到推送消息的目的,接下來我將寫出如何讓服務(wù)器建立客戶端與客戶端之間的通訊。
其實(shí)就是建立一個(gè)一對(duì)一的聊天通訊。
與上一篇實(shí)現(xiàn)消息推送的代碼有些不同,在它上面加以修改的。
如果沒有提到的方法或者類則和上一篇一模一樣。
1,修改實(shí)體類(服務(wù)器端和客戶端的實(shí)體類是一樣的)
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; // 用戶密碼 //省略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 String content; // 文本消息內(nèi)容 private String errorMsg; // 錯(cuò)誤信息 private int errorCode; // 錯(cuò)誤代碼 private int userId; //用戶id private int friendId; //目標(biāo)好友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; //文件內(nèi)容 private String fileType; //文件類型 private String fileTitle; //文件頭名稱 //省略get、set方法 } |
2,(服務(wù)器端代碼修改)ChatServer 主要的聊天服務(wù)類,加以修改
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服務(wù) // 使用ArrayList存儲(chǔ)所有的Socket public List<Socket> socketList = new ArrayList<>(); // 模仿保存在內(nèi)存中的socket public Map<Integer, Socket> socketMap = new HashMap(); // 模仿保存在數(shù)據(jù)庫中的用戶信息 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)聽客戶請(qǐng)求 server = new ServerSocket(SocketUrls.PORT); createMessage(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 創(chuàng)建消息管理,一直接收消息 */ private void createMessage() { try { System.out.println( "等待用戶接入 : " ); // 使用accept()阻塞等待客戶請(qǐng)求 Socket socket = server.accept(); // 將鏈接進(jìn)來的socket保存到集合中 socketList.add(socket); System.out.println( "用戶接入 : " + socket.getPort()); // 開啟一個(gè)子線程來等待另外的socket加入 new Thread( new Runnable() { public void run() { // 再次創(chuàng)建一個(gè)socket服務(wù)等待其他用戶接入 createMessage(); } }).start(); // 用于服務(wù)器推送消息給用戶 getMessage(); // 從客戶端獲取信息 BufferedReader bff = new BufferedReader( new InputStreamReader(socket.getInputStream())); // 讀取發(fā)來服務(wù)器信息 String line = null ; // 循環(huán)一直接收當(dāng)前socket發(fā)來的消息 while ( true ) { Thread.sleep( 500 ); // System.out.println("內(nèi)容 : " + bff.readLine()); // 獲取客戶端的信息 while ((line = bff.readLine()) != null ) { // 解析實(shí)體類 MessageBean messageBean = gson.fromJson(line, MessageBean. class ); // 將用戶信息添加進(jìn)入map中,模仿添加進(jìn)數(shù)據(jù)庫和內(nèi)存 // 實(shí)體類存入數(shù)據(jù)庫,socket存入內(nèi)存中,都以用戶id作為參照 setChatMap(messageBean, socket); // 將用戶發(fā)送進(jìn)來的消息轉(zhuǎn)發(fā)給目標(biāo)好友 getFriend(messageBean); System.out.println( "用戶 : " + userMap.get(messageBean.getUserId()).getUserName()); System.out.println( "內(nèi)容 : " + messageBean.getContent()); } } // server.close(); } catch (Exception e) { // TODO Auto-generated catch block e.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ù)器的用戶推送消息 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 block e.printStackTrace(); } } }).start(); } /** * 模擬添加信息進(jìn)入數(shù)據(jù)庫和內(nèi)存 * * @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())); } // 將對(duì)應(yīng)的鏈接進(jìn)來的socket存起來 if (socketMap != null && socketMap.get(messageBean.getUserId()) == null ) { socketMap.put(messageBean.getUserId(), scoket); } } /** * 模擬數(shù)據(jù)庫的用戶信息,這里創(chuàng)建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; } /** * 將消息轉(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 { // 向客戶端發(fā)送信息 OutputStream output = socket.getOutputStream(); output.write(buffer.getBytes( "utf-8" )); // 發(fā)送數(shù)據(jù) 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 , "密碼錯(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為用戶,2為另一個(gè)用戶,這里模擬出兩個(gè)用戶互相通訊 * * @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 ; //密碼錯(cuò)誤 } } /** * 實(shí)例化一個(gè)聊天服務(wù) * * @param status */ private void getChatServer( int status) { ChatAppliaction.chatServer = new ChatServer(status); } } |
4,(客戶端代碼)ChatServer 聊天服務(wù)代碼邏輯的修改
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對(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() { //開個(gè)線程接收消息 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" ); //以下操作模仿當(dāng)用戶點(diǎn)擊了某個(gè)好友展開的聊天界面,將保存用戶id和聊天目標(biāo)用戶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; } /** * 發(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-8 String 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() { @Override public void run() { try { // 向本機(jī)的8080端口發(fā)出客戶請(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()); // 從客戶端獲取信息 BufferedReader bff = new BufferedReader( new InputStreamReader(input)); // 讀取發(fā)來服務(wù)器信息 String line; while ( true ) { Thread.sleep( 500 ); // 獲取客戶端的信息 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; } } |
如此一來,代碼邏輯已經(jīng)從消息推送的邏輯修改成了單聊的邏輯了。
這個(gè)代碼可以讓用戶1和用戶2相互聊天,并且服務(wù)器會(huì)記錄下他們之間的聊天記錄。并且服務(wù)器還是擁有消息推送的功能。
以上所述是小編給大家介紹的Java Socket聊天室編程(二)之利用socket實(shí)現(xiàn)單聊聊天室,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://blog.csdn.net/yehui928186846/article/details/52583142