【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颗粒无收的大四狗
- 【Android】基于Socket的即时聊天(群聊)
- 基于 xmpp 的即时聊天
- Android即时通讯--仿QQ即时聊天:(一)初识Socket
- Android利用Socket(TCP)通信实现即时聊天
- Android 基于Socket的聊天应用(二)
- 基于WIFI direct的即时聊天app开发(一)
- 基于openfire+smack开发Android即时聊天应用[四]-单人聊天、群聊、发送接收文件等
- 基于openfire+smack开发Android即时聊天应用[四]-单人聊天、群聊、发送接收文件等
- 基于openfire+smack开发Android即时聊天应用[三]-单人聊天、群聊、发送接收文件等
- 基于openfire+smack开发Android即时聊天应用[四]-单人聊天、群聊、发送接收文件等
- 基于WebSocket的即时聊天程序
- 基于SocketChannel的即时聊天程序
- 基于Bmob的仿微信即时聊天软件
- 【Android】 基于Socket 的即时通信软件 YQ(源码下载)
- Android学习之Socket多个客户端即时通信聊天
- 基于百度推送的第三方服务器android即时聊天系统
- Android 基于openfire即时聊天开发遇到的闪退问题:Already Login to Server
- android基于openfire+spark+amack 即时聊天--------<2>登录遇到的问题和重要类解析
- 51nod-1105-第K大的数
- 集成框架spring integration体验
- pandas 多个列,行读取方法
- < 笔记 > Python
- 【二分+数学】bzoj1257: [CQOI2007]余数之和sum
- 【Android】基于Socket的即时聊天(群聊)
- js-es6-数值扩展
- HDOJ2056
- 把python基本功搞扎实(5)
- ZooKeeper(5)Curator介绍
- 【XSY1762】染色问题 网络流
- Java中Volatile关键字详解
- 礼物
- 大学考试题系列 权限管理