Android實現C/S聊天室

Java中能接受其他通信實體鏈接請求的類是ServerSocket,ServerSocket對象用於監聽來自客戶端的Socket鏈接,如果沒有鏈接,它將一直等待。如果接收到一個客戶端Socket的連接請求,ServerSocket的accept()方法將返回一個與客戶端Socket對應的Socket(每個TCP連接有兩個Socket),否則該方法將一直阻塞,線程也被阻塞。

服務端思路:服務端應該包含多個線程,每個Socket對應一個線程,這個線程負責讀取該Socket對應輸入流的數據(從客戶端發送過來的數據),並將讀到的數據向每個Socket輸出流發送一次(將一個客戶端發送過來的數據“廣播”給其他客戶端)。

服務端代碼:

//服務端主類
public class MyServer
{
    public static List<Socket> socketList = Collections.synchronizedList(new ArrayList<Socket>());
    public static void main(String[] args) throws IOException
    {
        ServerSocket ss = new ServerSocket(30000);
        while (true)
        {
            //此行代碼會阻塞,將一直等待別人的連接
            Socket s = ss.accept();
            socketList.add(s);
            //每當客戶端連接後啓動一個ServerThread線程爲該客戶端服務
            new Thread(new ServerThread(s)).start();
        }
    }
}
public class ServerThread implements Runnable
{
    //定義當前線程所處理的Socket
    Socket s = null;
    //該線程所處理的Socket對應的輸入流
    BufferedReader br = null;
    public ServerThread(Socket s) throws IOException
    {
        this.s = s;
        //初始化該Socket對應的輸入流
        br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    }

    @Override
    public void run()
    {
        try
        {
            String content = null;
            //採用循環不斷地從Socket中讀取客戶端發送來的數據
            while ((content = readFromClient()) != null)
            {
                //遍歷socketList中的每個Socket
                //將讀到的內容向每個Socket發送一次
                for (Socket s : MyServer.socketList)
                {
                    PrintStream ps = new PrintStream(s.getOutputStream());
                    ps.println(content);
                }
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    //定義讀取客戶端數據的方法
    private String readFromClient()
    {
        try
        {
            return br.readLine();
        }
        //如果捕獲到異常,則表明該Socket對應的客戶端已經關閉
        catch (IOException e)
        {
            //刪除該Socket
            MyServer.socketList.remove(s);
        }
        return null;
    }
}

客戶端思路:將用戶輸入的數據寫入Socket對應的輸入流中;開啓一個子線程讀取Socket對應輸入流中的數據(從服務端發送過來的數據),並通過Handler將讀取的數據發送到主線程來更新UI。

//用戶界面Activity
public class MainActivity extends Activity
{
    private EditText mReceiverMsg;
    private Button mSendBtn;
    private EditText mSendMsg;
    Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            Log.d("mainActivity" , "okk");
            mReceiverMsg.append(msg.obj.toString());
        }
    };
    private Socket s;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);

        initView();
        initSocket();
        mSendBtn.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View view)
            {
                sendData();
            }
        });
    }

    private void initSocket()
    {
        new Thread()
        {
            @Override
            public void run()
            {
                try
                {
                    s = new Socket("192.168.1.101" , 30000);
                    new Thread(new ClientThread(s , handler)).start();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private void initView()
    {
        mReceiverMsg = (EditText) findViewById(R.id.receiver_message);
        mSendMsg = (EditText) findViewById(R.id.send_message);
        mSendBtn = (Button) findViewById(R.id.send_button);
    }

    private void sendData()
    {
        try
        {
            //獲取該Socket對應的輸出流
            PrintStream ps = new PrintStream(s.getOutputStream());
            if (TextUtils.isEmpty(mSendMsg.getText()))
            {
                Toast.makeText(this , "請輸入信息" , Toast.LENGTH_LONG).show();
                return;
            }
            ps.println(mSendMsg.getText().toString());
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
public class ClientThread implements Runnable
{
    //該線程負責處理的Socket
    private Socket ss;
    //該線程所處理的Socket對應的輸入流
    BufferedReader br = null;
    Handler handler;
    public ClientThread(Socket s  , Handler handler) throws IOException
    {
        this.ss = s;
        this.handler = handler;
        br = new BufferedReader(new InputStreamReader(ss.getInputStream()));
    }

    @Override
    public void run()
    {
        try
        {
            String content = null;
            while ((content = br.readLine()) != null)
            {
                Message msg = new Message();
                msg.obj = content;
                handler.sendMessage(msg);
            }

        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

先運行上面程序中的MyServer類,該類運行只是作爲服務端。再啓動多個模擬器,運行安裝客戶端的程序作爲多個客戶端,然後可以再任何一個客戶端通過Edit輸入一些內容,點擊發送就可以在任何一個客戶端看到剛剛輸入的內容。

發佈了27 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章