Android聊天室

背景

最新在研究网络编程,感觉好好玩,然后玩得兴起就想做个聊天室来玩玩。这个聊天室叫 ChatRoom ,是一个基于 TCP 通信的聊天室,并且用到了一些 Material Design 的控件,之前一直都在赶项目都没时间学习 Material Design(其实不是没时间,是有些事情很烦,所以耽搁了),工作室的昭哥老说我,你基础工不扎实,嘿嘿。

基础

TCP 是一种可靠、必须连接才能通信的协议。因为需要连接才能通信,所以 TCP 通信严格区分客户端和服务器端,只有客户端连接了服务器端才能实现通信,服务器端不能连接客户端并且需要事先启动,等待客户端的请求。

附图

ChatRoom

好了,扯了辣么多,我们来看看 ChatRoom 是怎么实现的吧!先看看效果:

服务器端

/**
 * Created by kn on 2016/5/24.
 *
 * 聊天室主线程服务
 */
public class MyServerSocket {

    public static void main(String args[]){
        new ServerListener().start();
    }
}
/**
 * Created by kn on 2016/5/24.
 *
 * 聊天监听线程类
 */
public class ServerListener extends Thread {

    @Override
    public void run() {
        try {
            //端口号port:1~65535
            ServerSocket serverSocket = new ServerSocket(30000);
            while(true){
                //该方法会阻塞当前线程
                Socket socket = serverSocket.accept();
                //建立连接
                System.out.println("有客户端链接到本地的30000端口");
                //将socket传递给新的线程
                ChatSocket chatSocket = new ChatSocket(socket);
                chatSocket.start();
                ChatManager.getInstance().add(chatSocket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
/**
 * Created by kn on 2016/5/24.
 *
 * 聊天线程类
 */
public class ChatSocket extends Thread {

    Socket socket;

    public ChatSocket(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(socket.getInputStream(), "UTF-8"));
            String line = null;
            while ((line = br.readLine()) != null) {
                ChatManager.getInstance().publish(this, line);
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
    * 输出服务器返回的信息
    * @param message
    */
    public void showMessage(String message) {
        try {
            socket.getOutputStream().write((message + "\n").getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
    * 判断socket是否关闭
    * @return
    */  
    public boolean isSocketClosed(){
        return socket.isClosed();
    }
}
/**
 * Created by kn on 2016/5/24.
 *
 * 聊天室线程管理类
 */
public class ChatManager {

    private static ChatManager instance = new ChatManager();
    //聊天线程列表
    Vector<ChatSocket> vector = new Vector<>();

    private ChatManager() {
    }

    /**
     * 单例模式
     * @return
     */
    public static ChatManager getInstance() {
        if (instance == null) {
            synchronized (ChatManager.class) {
                if (instance == null) {
                    instance = new ChatManager();
                }
            }
        }
        return instance;
    }

    /**
     * 添加聊天线程
     *
     * @param chatSocket
     */
    public void add(ChatSocket chatSocket) {
        vector.add(chatSocket);
    }

    /**
     * 向聊天室的其他线程发布消息
     * @param chatSocket
     * @param message
     */
    public void publish(ChatSocket chatSocket, String message) {
        //遍历线程列表
        for (int i = 0; i < vector.size(); i++) {
            ChatSocket mChatSocket = vector.get(i);
            //判断是否是己线程
            if (!chatSocket.equals(mChatSocket)) {
                //判断该线程是否已经断开服务器的连接
                if (mChatSocket.isSocketClosed()) {
                    vector.remove(i);
                } else {
                    mChatSocket.showMessage(message);
                }
            }
        }
    }
}

写完以上代码,服务器端基本上完成了,其实现在,我们就可以玩聊天室了。打开多个 cmd 并输入 telnet localhost 30000 就可以连接服务器,然后你就可以在黑框下聊天了。

客户端

主界面,输入服务器的 IP 和聊天中显示的名字

public class MainActivity extends AppCompatActivity {

    MaterialEditText metName;//聊天的昵称
    MaterialEditText metIP;//聊天室的IP
    ImageView ivAvatar;//聊天的头像
    ButtonFlat btnEnter;//进入聊天按钮
    ProgressBar progressBar;//进度条
    FrameLayout flProgressBar;

    public static Socket socket = null;

    final static int SUCCESS = 0x01;//进入聊天室成功
    final static int FAILURE = 0x02;//进入聊天室失败
    final static int TIMEOUT = 0x03;//连接超时
    final static int UN_KNOWN_HOST = 0x04;//未知主机
    private BufferedWriter writer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        metName = (MaterialEditText) findViewById(R.id.met_name);
        metIP = (MaterialEditText) findViewById(R.id.met_ip);
        ivAvatar = (ImageView) findViewById(R.id.iv_avatar);
        btnEnter = (ButtonFlat) findViewById(R.id.btn_enter);
        progressBar = (ProgressBar) findViewById(R.id.progress);
        flProgressBar = (FrameLayout) findViewById(R.id.fl_progress);

        metName.setText("kntryer");
        metIP.setText("192.168.1.101");

        btnEnter.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                enterChatRoom();
            }
        });
    }

    private void enterChatRoom() {
        //获取ip
        final String ipString = metIP.getText().toString().trim();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    socket = new Socket();
                    socket.connect(new InetSocketAddress(ipString, 30000), 5000);
                    writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    handler.sendEmptyMessage(SUCCESS);
                } catch (SocketTimeoutException e) {
                    //连接超时 在UI界面显示消息
                    handler.sendEmptyMessage(TIMEOUT);
                    e.printStackTrace();
                } catch (UnknownHostException e) {
                    handler.sendEmptyMessage(UN_KNOWN_HOST);
                    e.printStackTrace();
                } catch (IOException e) {
                    handler.sendEmptyMessage(FAILURE);
                    e.printStackTrace();
                }
            }
        }).start();
    }

    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case SUCCESS:
                    String name = metName.getText().toString();
                    try {
                        if (writer != null) {
                            writer.write("system," + name + " enter ChatRoom\n");
                            writer.flush();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    Intent intent = new Intent(MainActivity.this, ChatActivity.class);
                    intent.putExtra("name", name);
                    startActivity(intent);
                    break;
                case FAILURE:
                    showIsOpenWifi("Please check whether the network is open or not.");
                    break;
                case TIMEOUT:
                    showIsOpenWifi("SocketTimeoutException");
                    break;
                case UN_KNOWN_HOST:
                    showIsOpenWifi("UnknownHostException");
                    break;

            }
            return false;
        }
    });

    private void showIsOpenWifi(String message) {
        new SnackBar(this, message, "Yes", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //跳到WiFi,这个还没做
            }
        }).show();
    }
}

聊天界面

/**
 * Created by kn on 2016/5/24.
 */
public class ChatActivity extends AppCompatActivity {

    ButtonFlat btnSent;//发送消息
    MaterialEditText metMsg;//消息
    ListView lvMsg;//显示消息

    ArrayList<ChatMessage> msgList = new ArrayList<>();//消息列表
    MessageAdapter adapter;
    Socket socket;
    String name;//聊天昵称
    BufferedReader reader;//读取服务器返回的数据
    BufferedWriter writer;//向服务器发送数据

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
        initView();
    }

    private void initView() {

        name = getIntent().getStringExtra("name");
        socket = MainActivity.socket;

        btnSent = (ButtonFlat) findViewById(R.id.btn_sent);
        metMsg = (MaterialEditText) findViewById(R.id.met_msg);
        lvMsg = (ListView) findViewById(R.id.lv_msg);

        adapter = new MessageAdapter(this,msgList);
        lvMsg.setAdapter(adapter);

        //开启一个新的线程监听聊天室,并获取聊天室的消息
        getMessage();

        btnSent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                ChatMessage chatMessage = new ChatMessage();
                chatMessage.setType(2);
                chatMessage.setName(name);
                chatMessage.setMessage(metMsg.getText().toString());
                msgList.add(chatMessage);
                adapter.notifyDataSetChanged();

                String message = name + "," + metMsg.getText().toString();
                metMsg.setText("");
                setMessage(message);
            }
        });
    }
    /**
     * 发送消息
     * @param message
     */
    private void setMessage(String message) {
        try {
            if (writer != null) {
                writer.write(message + "\n");
                writer.flush();
            } else {
                Toast.makeText(ChatActivity.this, "你已经离开 YY 聊天室!", Toast.LENGTH_LONG).show();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取聊天室的消息
     */
    private void getMessage() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    String line = null;
                    while ((line = reader.readLine()) != null) {

                        ChatMessage chatMessage = new ChatMessage();
                        String[] temp = line.split(",");
                        System.out.print(temp[0]);
                        if(temp[0].equals("system")){//如果temp[0]=system说明是系统消息
                            chatMessage.setType(0);
                            chatMessage.setMessage(temp[1]);
                        }else{
                            chatMessage.setType(1);
                            chatMessage.setName(temp[0]);
                            chatMessage.setMessage(temp[1]);
                        }
                        msgList.add(chatMessage);
                        handler.sendEmptyMessage(0x00);
                    }
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            adapter.notifyDataSetChanged();
            return false;
        }
    });

}

如何查看服务器 IP 呢?

打开 cmd ,输入 ipconfig ,就可以看到本机的 IP 设置,在无线局域网适配 wifi 这里的 IPv4地址 就是服务器的 IP 了。

模拟器可以连上服务器,手机却连不上怎么办?

网上说是手机是外网,访问不了内网的问题,手机和电脑连同一个 wifi 就行了。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值