【Android】基于Socket的即时聊天(群聊)

来源:互联网 发布:淘宝双十一魔盒怎么用 编辑:程序博客网 时间:2024/06/06 12:34

近来感觉秋招无望,学习Socket的时候,便做了个基于Socket的群聊工具;
先看看最终效果吧
这里写图片描述

这里写图片描述
这里写图片描述

项目GitHub通道(详细代码请自行copy)

如何利用Socket通信

socket又称为“套接字”,建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。 ——来自百度百科

socket通信步骤(TCP)
1.建立SocketServer(服务端) 和 Socket (客户端)
2.打开两个端之间的输入输出流
3.进行读写操作
4.关闭socket与流

先来看第一步
SocketServer 与 Socket都是 java.net包里的,进行TCP通信的时候需要建立客户端与服务端。SocketServer 通过绑定端口(Port)来实现监听,而Socket则是指定服务端端口(Port)与地址(IP)。
下面来看一个简单的通信例子

//Server部分ServerSocket server = new ServerSocket(6063);//实例化(传入端口号)Socket s = server.accept();//调用accept接收socketBufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));//获得socket的输入流(同样的,可以通过getOutputStream来获取输出流)while((msg= in.readLine())!=null){    System.out.println(msg);    }in.close();s.close();//ClientSocket s = new Socket("192.168.1.133",6063);//实例化Socket传入指定服务端地址和端口号            System.out.println("客户端启动...");            BufferedReader re = new BufferedReader(new InputStreamReader(System.in));//用输入流读取键盘的输入            PrintWriter pw = new PrintWriter(s.getOutputStream(),true);//获取socket的输出流,第二个参数表示会自动flush            String msg2;            while(true){                msg2 = re.readLine();                pw.println(msg2);//输出(自动flush)            }

这样我们就简单了实现了socket通信
当然要实现即时聊天并非这么几行就能搞定的。
即时聊天,重点在于流的控制,需要开辟多条线程去分别做不同的事情。
下面来本次项目的PC服务端代码

PCServer端代码

public class Server implements Runnable {    List<Socket> sockets = new ArrayList<>();    private static final String ServerIp = "192.168.1.133";    private static final int ServerPort = 6066;    @Override    public void run() {        try{            System.out.println("服务端启动...");            ServerSocket server = new ServerSocket(ServerPort);            while(true){                Socket client = server.accept();                sockets.add(client);                receiveThread re = new receiveThread(client);                re.start();            }        }catch(Exception e){            System.out.println("------S:Error 1-------");            e.printStackTrace();        }    }    //接受msg线程    public class receiveThread extends Thread{        Socket socket;        private BufferedReader br;        private PrintWriter pw;        public String msg;        public receiveThread(Socket s){            socket = s;        }        public void run(){            try{                br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));//不转码在Android端会乱码                msg = "sys:##:"+"欢迎"+socket.getInetAddress()+"进入聊天室,当前人数为"+sockets.size();                sendMsg(InetAddress.getByName("1"));                while((msg = br.readLine())!=null){                    if(msg.equals("EndEndClosethesocket")){                        close(socket.getInetAddress());                    }else{                        msg = socket.getInetAddress()+":##:"+msg;                        sendMsg(socket.getInetAddress());                    }                }            }catch(Exception e){                e.printStackTrace();            }        }        public void sendMsg(InetAddress ip){            try{                System.out.println(msg);                for(int i = 0;i < sockets.size();i++){                    if(!ip.equals(sockets.get(i).getInetAddress())){                    pw = new PrintWriter(new OutputStreamWriter(sockets.get(i).getOutputStream(),"UTF-8"),true);                    pw.println(msg);                    pw.flush();                    }                }            }catch(Exception e){                e.printStackTrace();            }        }    public void close(InetAddress ip){        for(int i = 0;i < sockets.size();i++){            if(sockets.get(i).getInetAddress()==ip){                sockets.remove(i);                msg ="sys:##:"+ip+"已经离开了聊天室";                try{                    sendMsg(InetAddress.getByName("1"));                }catch(Exception e){                    e.printStackTrace();                }                break;            }        }    }    }    public static void main(String args[]){        Thread thread = new Thread(new Server());        thread.start();    }}

可以看到,这里用了一个ArrayList来存储SocketServer接收的Socket
然后将接收的Socket作为参数传入自定义的线程receiveThread中,然
后在这个线程中循环读取Client端发来的消息。然后通过sendMsg方法
广播这条消息。
下面看看Android客户端的主要代码

Android端核心代码

  protected void onCreate(Bundle savedInstanceState) {       super.onCreate(savedInstanceState);       ...//详细代码请移步GitHub                 if(socket==null||socket.isClosed()||!socket.isConnected()){            getIPandPort();            creatSock();            Log.d("aaaaaaa", "onCreate: 1111111111");        }else {            Log.d("aaaaaaa", "onCreate: 122222222222");            heart();        }        send.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //发送button监听                if(sendMsg(edit.getText().toString())){                    edit.setText("");                }            }        });  }   //读取ip与Port    public void getIPandPort(){        SharedPreferences preferences = getSharedPreferences("data", Context.MODE_PRIVATE);        ServerIp = preferences.getString("ip","192.168.1.133");        ServerPort = preferences.getInt("port",6066);        title.setText(ServerIp+"\n"+ServerPort);    }    //心跳检测 public void heart(){        if (socket!=null){            try{                socket.sendUrgentData(0xff);            }catch (Exception e){                e.printStackTrace();                reconn();            }        }else {            reconn();        }    }    //建立连接    public void creatSock(){        new Thread(new Runnable() {            @Override            public void run() {                try{//                    if (socket!=null){//                       clconn();//                    }                    InetAddress inetAddress = InetAddress.getByName(ServerIp);                    socket = new Socket(inetAddress, ServerPort);                    AcceptMsg();                }catch (Exception e) {                    e.printStackTrace();                    reconn();                    Log.d("aaaa", "run: 连接失败");                }            }        }).start();    }    public void reconn(){        sendMessenger(new Msg("无法连接服务器...请重设PORT或IP",2,"sys"));        showReconn();    }     public  void sendMessenger(final Msg msg){        runOnUiThread(new Runnable() {            @Override            public void run() {                msgs.add(msg);                adapter.notifyDataSetChanged();            }        });    }    //发送    private boolean sendMsg(final String msg){        if(socket!=null&&socket.isConnected()){            if (!msg.equals("")){                new Thread(new Runnable() {                    @Override                    public void run() {                        try{                            pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);                            pw.println(msg);                            if (!msg.equals("EndEndClosethesocket")){                                sendMessenger(new Msg(msg,0,"Me"));                            }                            Log.d("XXXXXXX", "发送成功");                        }catch (Exception e) {                            heart();                        }                    }                }).start();                return true;            }        }        return false;    }    //接受    public void AcceptMsg(){        if (socket.isConnected()&&!socket.isClosed()){            new Thread(new Runnable() {                @Override                public void run() {                    try{                        re = new BufferedReader(new InputStreamReader(socket.getInputStream()));                        while((socketmsg=re.readLine())!=null){                            String[] msg = socketmsg.split(":##:");                            Msg m;                            if(msg[0].equals("sys")){                                m = new Msg(msg[1],2,msg[0]);                            }else{                                Log.d("sxxxxx", "run: "+msg[0]);                                String ip = msg[0].substring(msg[0].length()-3,msg[0].length());                                m = new Msg(msg[1],1,ip);                            }                            sendMessenger(m);                            Log.d("xxxxxxxxx", "接受成功"+socketmsg);                        }                    }catch (Exception e){                        e.printStackTrace();                        heart();                        Log.d("aaaa", "run: 接受失败");                    }                }            }).start();        }        heart();    }

在安卓端实例化Socket时要注意将IP地址转化为InetAddress,再将参数传入。
另外,Socket操作不能在主线程里直接操作,否则会报错,应该新建线程对其
进行操作(无论是建立连接,发送消息,还是接收消息);
本次项目开发也遇到了许多坑。
一开始想用ViewPage+fragement来实现更好的界面效果,由于fragment的
重载的大坑,导致我最终放弃直接用fragment实现聊天界面
还有就是心跳包的问题,重复发送多次会导致服务端崩溃,捣鼓了一下午也没
弄好,最后还是没用上。
这次就到这里咯,收拾收拾好心情,面对十月的秋招吧!!!!

——-来自offer颗粒无收的大四狗

阅读全文
1 0
原创粉丝点击