Android Socket實(shí)現(xiàn)多個(gè)客戶端即時(shí)通信聊天
聊天室的原理是這樣的,一個(gè)客戶端與服務(wù)器建立通訊成功,即客戶端socket連接到服務(wù)器的ServerSocket之后,服務(wù)器端程序?qū)?duì)應(yīng)的socket加入到容器中,為每一個(gè)socket創(chuàng)建一條線程,服務(wù)器端讀到客戶端發(fā)來的信息之后,遍歷該容器,給容器中的每個(gè)socket發(fā)送一次,就完成了所有客戶端廣播。當(dāng)客戶端離開時(shí),向服務(wù)器發(fā)送一條斷開連接信息,從服務(wù)器端斷開socket,將容器中的對(duì)應(yīng)socket移除掉。
圖:
服務(wù)器端Server,java:
accept通信后,為該socket建立一條線程,開啟循環(huán)讀寫操作
/** ?* Created by konghao on 2017/11/8. ?*/ public class Server { ? ? ? static List<Socket> cons = new LinkedList<Socket>(); ? ? private static Socket socket = null; ? ? ? public static class ServerThread extends Thread{ ? ? ? ? private Socket s; ? ? ? ? ? public ServerThread(Socket socket){ ? ? ? ? ? ? this.s = socket; ? ? ? ? ? ? cons.add(s); ? ? ? ? } ? ? ? ? ? @Override ? ? ? ? public void run(){ ? ? ? ? ? ? System.out.print("新用戶加入\n"); ? ? ? ? ? ? System.out.print("當(dāng)前在線數(shù)量:"+cons.size()+"\n"); ? ? ? ? ? ? try{ ? ? ? ? ? ? ? ? while(true){ ? ? ? ? ? ? ? ? ? ? ? BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); ? ? ? ? ? ? ? ? ? ? String mess;// = br.readLine(); ? ? ? ? ? ? ? ? ? ? //保存信息 ? ? ? ? ? ? ? ? ? ? if((mess=br.readLine())!=null) { ? ? ? ? ? ? ? ? ? ? ? ? if(mess.equals("-用戶退出-")){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? s.close(); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? System.out.print("客戶端:" + mess + "\n"); ? ? ? ? ? ? ? ? ? ? ? ? BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); ? ? ? ? ? ? ? ? ? ? ? ? //為了證明是服務(wù)器返回的數(shù)據(jù),我對(duì)mess修改在發(fā)送到客戶端 ? ? ? ? ? ? ? ? ? ? ? ? //這里修改廣播到所有客戶端 ? ? ? ? ? ? ? ? ? ? ? ? for (Socket so : cons){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? BufferedWriter buffw = new BufferedWriter(new OutputStreamWriter(so.getOutputStream())); ? ? ? ? ? ? ? ? ? ? ? ? ? ? String str = "服務(wù)器>>"+mess+"\n"; ? ? ? ? ? ? ? ? ? ? ? ? ? ? buffw.write(str); ? ? ? ? ? ? ? ? ? ? ? ? ? ? buffw.flush(); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? // ? ? ? ? ? ? ? ? ? ? ? ? //單客戶端通信 ? ? ? ? ? ? ? ? ? ? ? ? /* ? ? ? ? ? ? ? ? ? ? ? ? String str = "服務(wù)器>>"+mess+"\n"; ? ? ? ? ? ? ? ? ? ? ? ? bw.write(str); ? ? ? ? ? ? ? ? ? ? ? ? bw.flush(); ? ? ? ? ? ? ? ? ? ? ? ? */ ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? }catch (IOException e){ ? ? ? ? ? ? ? ? System.out.print("用戶退出!\n"); ? ? ? ? ? ? ? ? cons.remove(s); ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? this.interrupt(); ? ? ? ? ? ? ? ? //e.printStackTrace(); ? ? ? ? ? ? }catch (NullPointerException e) { ? ? ? ? ? ? ? ? System.out.print("NullPointerException"); ? ? ? ? ? ? }finally { ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? s.close(); ? ? ? ? ? ? ? ? }catch (IOException e){ ? ? ? ? ? ? ? ? ? ? System.out.print("IOException-2"); ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? } ? ? ? public static void main(String[] args) throws IOException { ? ? ? ? ? ServerSocket ss = new ServerSocket(30001); ? ? ? ? while(true){ ? ? ? ? ? ? Socket s = ss.accept(); ? ? ? ? ? ? new Thread(new ServerThread(s)).start(); ? ? ? ? } ? ? } }
安卓客戶端:
進(jìn)入活動(dòng)時(shí)候創(chuàng)建一條線程,完成socket連接,和循環(huán)監(jiān)聽接受服務(wù)器發(fā)來的消息。當(dāng)發(fā)送按鈕按下后,向服務(wù)器發(fā)送消息。退出活動(dòng)時(shí)候,向服務(wù)器發(fā)送結(jié)束通信信息,服務(wù)器收到后關(guān)閉socket。
ChatRoomActivity.java
public class ChatRoomActivity extends Activity implements View.OnClickListener{ ? ? private Socket socket = null; ? ? private EditText edit; ? ? private Button send,disconnect; ? ? private RecyclerView msg_recyclerView; ? ? private MsgAdapter adapter; ? ? private List<Msg> msgs = new ArrayList<Msg>();//存儲(chǔ)消息容器 ? ? private String name = ""; ? ? private static final int UPDATE = 1; ? ? ? @Override ? ? protected void onCreate(Bundle savedInstanceState) { ? ? ? ? super.onCreate(savedInstanceState); ? ? ? ? setContentView(R.layout.activity_chat_room); ? ? ? ? ? Intent intent = getIntent(); ? ? ? ? name = intent.getStringExtra("name"); ? ? ? ? Log.d("孔昊",name); ? ? ? ? ? //從服務(wù)器讀數(shù)進(jìn)程 ? ? ? ? new Thread(new Runnable() { ? ? ? ? ? ? private String msg_get=""; ? ? ? ? ? ? @Override ? ? ? ? ? ? public void run() { ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? socket = new Socket(); ? ? ? ? ? ? ? ? ? ? socket.connect(new InetSocketAddress("公網(wǎng)IP", 30001), 5000); ? ? ? ? ? ? ? ? ? ? ? //input ? ? ? ? ? ? ? ? ? ? BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); ? ? ? ? ? ? ? ? ? ? ? while((msg_get=br.readLine())!=null){ ? ? ? ? ? ? ? ? ? ? ? ? Message message = new Message(); ? ? ? ? ? ? ? ? ? ? ? ? Bundle bundle = new Bundle(); ? ? ? ? ? ? ? ? ? ? ? ? Log.d("孔昊",msg_get); ? ? ? ? ? ? ? ? ? ? ? ? bundle.putString("msg",msg_get); ? ? ? ? ? ? ? ? ? ? ? ? message.setData(bundle); ? ? ? ? ? ? ? ? ? ? ? ? message.what = UPDATE; ? ? ? ? ? ? ? ? ? ? ? ? handler.sendMessage(message); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? }catch (IOException e){ ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? }).start(); ? ? ? ? ? edit = (EditText) findViewById(R.id.edit); ? ? ? ? send = (Button) findViewById(R.id.send); ? ? ? ? send.setOnClickListener(this); ? ? ? ? disconnect = (Button) findViewById(R.id.disconnect); ? ? ? ? disconnect.setOnClickListener(this); ? ? ? ? ? msg_recyclerView = (RecyclerView) findViewById(R.id.msg_view); ? ? ? ? LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); ? ? ? ? msg_recyclerView.setLayoutManager(linearLayoutManager); ? ? ? ? adapter = new MsgAdapter(msgs); ? ? ? ? msg_recyclerView.setAdapter(adapter); ? ? } ? ? ? Handler handler = new Handler(){ ? ? ? ? ? @Override ? ? ? ? public void handleMessage(Message msg){ ? ? ? ? ? ? super.handleMessage(msg); ? ? ? ? ? ? switch (msg.what){ ? ? ? ? ? ? ? ? //更新消息列表 ? ? ? ? ? ? ? ? case UPDATE:{ ? ? ? ? ? ? ? ? ? ? Bundle bundle = msg.getData(); ? ? ? ? ? ? ? ? ? ? String msg_get = bundle.getString("msg"); ? ? ? ? ? ? ? ? ? ? Msg msg1 = new Msg(msg_get); ? ? ? ? ? ? ? ? ? ? msgs.add(msg1); ? ? ? ? ? ? ? ? ? ? adapter.notifyDataSetChanged(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? } ? ? }; ? ? ? //向服務(wù)器發(fā)數(shù)據(jù)進(jìn)程 ? ? class SocketThread extends Thread{ ? ? ? ? private String msg; ? ? ? ? //Socket socket; ? ? ? ? ? public SocketThread(String m){ ? ? ? ? ? ? msg = m; ? ? ? ? } ? ? ? ? ? @Override ? ? ? ? public void run(){ ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); ? ? ? ? ? ? ? ? bw.write(msg); ? ? ? ? ? ? ? ? bw.newLine(); ? ? ? ? ? ? ? ? bw.flush(); ? ? ? ? ? ? ? }catch (IOException e){ ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? } ? ? ? } ? ? ? @Override ? ? public void onClick(View v) { ? ? ? ? switch(v.getId()){ ? ? ? ? ? ? case R.id.send:{ ? ? ? ? ? ? ? ? String s = name +":"+ edit.getText().toString(); ? ? ? ? ? ? ? ? edit.setText(""); ? ? ? ? ? ? ? ? new Thread(new SocketThread(s)).start(); ? ? ? ? ? ? } ? ? ? ? ? ? break; ? ? ? ? ? ? case R.id.disconnect:{ ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? //socket.shutdownOutput(); ? ? ? ? ? ? ? ? ? ? //socket.shutdownInput(); ? ? ? ? ? ? ? ? ? ? //socket.close(); ? ? ? ? ? ? ? ? ? ? BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); ? ? ? ? ? ? ? ? ? ? bw.write("-用戶退出-"); ? ? ? ? ? ? ? ? ? ? bw.newLine(); ? ? ? ? ? ? ? ? ? ? bw.flush(); ? ? ? ? ? ? ? ? ? ? //發(fā)送message,更新UI ? ? ? ? ? ? ? ? ? ? Message message = new Message(); ? ? ? ? ? ? ? ? ? ? Bundle bundle = new Bundle(); ? ? ? ? ? ? ? ? ? ? bundle.putString("msg","你已經(jīng)退出聊天!"); ? ? ? ? ? ? ? ? ? ? message.setData(bundle); ? ? ? ? ? ? ? ? ? ? message.what = UPDATE; ? ? ? ? ? ? ? ? ? ? handler.sendMessage(message); ? ? ? ? ? ? ? ? ? }catch (IOException e){ ? ? ? ? ? ? ? ? ? ? //Log.d("孔昊","斷開連接"); ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? break; ? ? ? ? } ? ? } ? ? ? //back事件 ? ? ? @Override ? ? public void onBackPressed(){ ? ? ? ? try { ? ? ? ? ? ? BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); ? ? ? ? ? ? bw.write("-用戶退出-"); ? ? ? ? ? ? bw.newLine(); ? ? ? ? ? ? bw.flush(); ? ? ? ? ? }catch (IOException e){ ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? } ? ? ? ? super.onBackPressed(); ? ? } ? }
服務(wù)器端收到的信息:
另附j(luò)ava控制臺(tái)作為客戶端代碼:
/** ?* Created by konghao on 2017/11/8. ?*/ public class Client { ? ? private static String name; ? ? private static Socket socket; ? ? public static void main(String[] args) { ? ? ? ? try { ? ? ? ? ? ? name = "孔昊"; ? ? ? ? ? ? Socket s = new Socket("112.74.92.125",30001); ? ? ? ? ? ? socket = s; ? ? ? ? ? ? System.out.println("客戶端IP:"+s.getLocalAddress()+"端口"+s.getPort()); ? ? ? ? ? ? //構(gòu)建IO流 ? ? ? ? ? ? BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); ? ? ? ? ? ? ? //建立鍵盤輸入: ? ? ? ? ? ? Scanner scanner = new Scanner(System.in); ? ? ? ? ? ? while(true){ ? ? ? ? ? ? ? ? System.out.println("請(qǐng)輸入發(fā)送消息內(nèi)容:"); ? ? ? ? ? ? ? ? bw.write(name+":"+scanner.nextLine()+"\n"); ? ? ? ? ? ? ? ? bw.newLine(); ? ? ? ? ? ? ? ? bw.flush(); ? ? ? ? ? ? ? ? BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); ? ? ? ? ? ? ? ? //讀取服務(wù)器返回的消息數(shù)據(jù) ? ? ? ? ? ? ? ? System.out.println(s.getInetAddress().getLocalHost()+":"+s.getPort()+">>"+br.readLine()); ? ? ? ? ? ? } ? ? ? ? ? } catch (UnknownHostException e) { ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? }finally { ? ? ? ? ? ? try{ ? ? ? ? ? ? ? ? socket.close(); ? ? ? ? ? ? }catch (IOException e){ ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? } ? ? ? } }
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android實(shí)現(xiàn)定時(shí)自動(dòng)靜音小助手
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)定時(shí)自動(dòng)靜音小助手,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06Flutter實(shí)現(xiàn)簡單的內(nèi)容高亮效果
內(nèi)容高亮并不陌生,特別是在搜索內(nèi)容頁面,可以說四處可見,這篇文章主要為大家介紹了如何使用Flutter實(shí)現(xiàn)簡單的內(nèi)容高亮效果,需要的可以參考下2023-08-08Android實(shí)現(xiàn)打開各種文件的intent方法小結(jié)
這篇文章主要介紹了Android實(shí)現(xiàn)打開各種文件的intent方法,結(jié)合實(shí)例形式總結(jié)分析了Android針對(duì)HTML、圖片文件、pdf文件、文本文件、音頻文件、視頻文件等的intent打開方法,需要的朋友可以參考下2016-08-08Android自定義View實(shí)現(xiàn)圓形加載進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)圓形加載進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Android XmlPullParser 方式解析 Xml 文檔
這篇文章主要介紹了Android XmlPullParser 方式解析 Xml 文檔的相關(guān)資料,需要的朋友可以參考下2017-06-06Android中的ViewPager視圖滑動(dòng)切換類的入門實(shí)例教程
Android中ViewPager通常與Fragments組件共同使用來實(shí)現(xiàn)視圖切換功能,本文就帶大家一起來學(xué)習(xí)Android中的ViewPager視圖滑動(dòng)切換類的入門實(shí)例教程:2016-06-06Android手動(dòng)檢查并申請(qǐng)權(quán)限方法
今天小編就為大家分享一篇Android手動(dòng)檢查并申請(qǐng)權(quán)限方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07解決Android studio xml界面無法預(yù)覽問題
這篇文章主要介紹了解決Android studio xml界面無法預(yù)覽問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03Android動(dòng)態(tài)給ViewPager添加Indicator導(dǎo)航
這篇文章主要為大家詳細(xì)介紹了Android動(dòng)態(tài)給ViewPager添加Indicator導(dǎo)航的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02