在上篇文章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; // 用戶密碼 //省略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; // 文本消息內容 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服務 // 使用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