这几天学习了下在android中实现即时通讯的方法,一开始,自然是从基本的网络协议中开始尝试了,这样能够最大化的私人订制自己的应用,还能学习到更多的知识,好处多多,接下来就简单介绍下两种协议的不同点吧
TCP协议:提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。就如给悬崖上的两人通信时,他必须先把桥建好,确认桥是没问题的情况下,才把信件交过去,以后大家每次通信时,都确认下桥没什么问题,再通过这座桥来回通信了。
UDP协议:不为IP提供可靠性、流控或差错恢复功能,在正式通信前不必与对方先建立连接,不管对方状态就直接发送。这个就是飞鸽传书了~
虽然UDP可靠性不如TCP协议,但是通信效率高于TCP。在网速极差的情况下优先考虑UDP协议,网速好的话TCP还是很方便使用的。
在Java中使用TCP可以通过java.net.Socket;这个类
<span style="font-family:Microsoft YaHei;font-size:18px;">建立连接</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//实例化一个Socket对象 socket = new Socket(); //与对应的ip、端口进行连接,先要把桥建好 socket.connect(new InetSocketAddress(ip, port), 3000);</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">发送信息</span>
</pre><pre name="code" class="java"><span style="font-family:Microsoft YaHei;font-size:18px;">InputStream ois = socket.getInputStream(); DataInputStream dis = new DataInputStream(new BufferedInputStream(ois)); //读取服务器发过来的信息,如果没信息将会阻塞线程 msg = dis.readUTF();</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">发送信息</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//获得输出流 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); //发送数据 dos.writeUTF(msg);</span>
接下来上源码,为三个Thread的子类,分别对应上面三个
<span style="font-family:Microsoft YaHei;font-size:18px;">public class SocketThread extends Thread{ private Socket socket; private Client client; private String ip; private int port; private boolean isStart=false; private MessageListener mMessageListener; /** * * 使用TCP协议,连接访问 * @param ip 目标机器的IP * @param port 端口 * @param mMessageListener 收到服务器端数据时将回调该接口内的 * public void Message(String msg)方法 */ public SocketThread(String ip, int port, MessageListener mMessageListener) { this.ip = ip; this.port = port; this.mMessageListener = mMessageListener; } public void run() { try { //实例化一个Socket对象 socket = new Socket(); //与对应的ip、端口进行连接,先要把桥建好 socket.connect(new InetSocketAddress(ip, port), 3000); if (socket.isConnected()) { System.out.println("Connected.."); client = new Client(socket,mMessageListener); //打开对应的输入/输出流监听 client.start(); isStart=true; } } catch (IOException e) { e.printStackTrace(); isStart=false; } } // 直接通过client得到读线程 public ClientInputThread getClientInputThread() { return client.getIn(); } // 直接通过client得到写线程 public ClientOutputThread getClientOutputThread() { return client.getOut(); } //返回Socket状态 public boolean isStart(){ return isStart; } // 直接通过client停止读写消息 public void setIsStart(boolean isStart) { this.isStart = isStart; client.getIn().setStart(isStart); client.getOut().setStart(isStart); } //发送消息 public void sendMsg(String msg){ client.getOut().sendMsg(msg); } public class Client { private ClientInputThread in; private ClientOutputThread out; public Client(Socket socket,MessageListener mMessageListener) { //用这个监听输入流线程来接收信息 in = new ClientInputThread(socket); in.setMessageListener(mMessageListener); //以后就用这个监听输出流的线程来发送信息了 out = new ClientOutputThread(socket); } public void start() { in.setStart(true); out.setStart(true); in.start(); out.start(); } // 得到读消息线程 public ClientInputThread getIn() { return in; } // 得到写消息线程 public ClientOutputThread getOut() { return out; } } }</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">public class ClientInputThread extends Thread { private Socket socket; private String msg; private boolean isStart = true; private InputStream ois; private DataInputStream dis; private MessageListener messageListener;// 消息监听接口对象 public ClientInputThread(Socket socket) { this.socket = socket; try { ois = socket.getInputStream(); dis = new DataInputStream(new BufferedInputStream(ois)); } catch (IOException e) { e.printStackTrace(); } } /** * 提供给外部的消息监听方法 * * @param messageListener * 消息监听接口对象 */ public void setMessageListener(MessageListener messageListener) { this.messageListener = messageListener; } public void setStart(boolean isStart) { this.isStart = isStart; } @Override public void run() { try { while (isStart) { //读取信息,如果没信息将会阻塞线程 msg = dis.readUTF(); // 每收到一条消息,就调用接口的方法Message(String msg) Log.v("收到消息", msg); messageListener.Message(msg); } ois.close(); if (socket != null) socket.close(); } catch (IOException e) { e.printStackTrace(); } } BufferedReader reader=null; public String getInputStreamString() { /* * To convert the InputStream to String we use the * BufferedReader.readLine() method. We iterate until the BufferedReader * return null which means there's no more data to read. Each line will * appended to a StringBuilder and returned as String. */ if (ois != null) { reader = new BufferedReader(new InputStreamReader(ois)); } StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } return sb.toString(); } }</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">public class ClientOutputThread extends Thread { private Socket socket; private DataOutputStream dos; private boolean isStart = true; private String msg; public ClientOutputThread(Socket socket) { this.socket = socket; try { dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); } catch (IOException e) { e.printStackTrace(); } } public void setStart(boolean isStart) { this.isStart = isStart; } // 这里处理跟服务器是一样的 public void sendMsg(String msg) { this.msg = msg; synchronized (this) { notifyAll(); } } @Override public void run() { try { while (isStart) { if (msg != null) { dos.writeUTF(msg); dos.flush(); msg=null; synchronized (this) { wait();// 发送完消息后,线程进入等待状态 } } } dos.close();// 循环结束后,关闭输出流和socket if (socket != null) socket.close(); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//定义接收到消息时的,处理消息的接口 public interface MessageListener { public void Message(String msg); }</span>
主界面,感觉很丑,将就吧
<span style="font-family:Microsoft YaHei;font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.chatclient.MainActivity" > <ScrollView android:id="@+id/svMessage" android:layout_width="fill_parent" android:layout_height="100dp" > <TextView android:id="@+id/tvMessage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="消息内容:\n" /> </ScrollView> <EditText android:id="@+id/etMessage" android:layout_width="100dp" android:layout_height="wrap_content" android:layout_below="@+id/svMessage" /> <TextView android:id="@+id/tvSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/svMessage" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/etMessage" android:text="发送消息" android:textSize="25sp" /> " </RelativeLayout></span>
MainActivity代码
<span style="font-family:Microsoft YaHei;font-size:18px;">public class MainActivity extends Activity { EditText etMessage; TextView tvSend, tvMessage; SocketThread client; MyHandler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setup(); } public void setup() { etMessage = (EditText) findViewById(R.id.etMessage); tvSend = (TextView) findViewById(R.id.tvSend); tvMessage = (TextView) findViewById(R.id.tvMessage); tvSend.setOnClickListener(onClick); myHandler = new MyHandler(); //初始化 client = new SocketThread("10.21.56.226", 8888,new MessageListener() { //收到消息后调用此方法 @Override public void Message(String msg) { // TODO Auto-generated method stub // tvMessage.append(msg); Bundle bundle = new Bundle(); bundle.putString("input", msg); Message isMessage = new Message(); isMessage.setData(bundle); //使用handler转发 myHandler.sendMessage(isMessage); } }); //正式启动线程 client.start(); } OnClickListener onClick = new OnClickListener() { public void onClick(android.view.View v) { String message = etMessage.getText().toString(); Log.v("发送消息", message); if (client.isStart()) { client.sendMsg(message); } }; }; private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub Log.v("处理收到的消息", " "); tvMessage.append(msg.getData().getString("input")); } } }</span>
服务器端的代码,主要是使用ServerSocket监听一个端口,来与客户端链接和收发信息
<span style="font-family:Microsoft YaHei;font-size:18px;">public class ChatServer { boolean started = false; ServerSocket ss = null; List<Client> clients = new ArrayList<Client>(); public static void main(String[] args) { new ChatServer().start(); } public void start() { try { //ServerSocket监听8888端口 ss = new ServerSocket(8888); started = true; } catch (BindException e) { System.out.println("start...."); System.out.println("有问题"); e.printStackTrace(); System.exit(0); } catch (IOException e) { e.printStackTrace(); } try { while (started) { Socket s = ss.accept(); Client c = new Client(s); System.out.println("a client connected!"); new Thread(c).start(); clients.add(c); // dis.close(); } } catch (IOException e) { e.printStackTrace(); } finally { try { ss.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class Client implements Runnable { private Socket s; private DataInputStream dis = null; private DataOutputStream dos = null; private boolean bConnected = false; public Client(Socket s) { this.s = s; try { dis = new DataInputStream(s.getInputStream()); dos = new DataOutputStream(s.getOutputStream()); bConnected = true; } catch (IOException e) { e.printStackTrace(); } } public void send(String str) { try { dos.writeUTF(str); } catch (IOException e) { clients.remove(this); System.out.println("关闭一个连接"); // e.printStackTrace(); } } public void run() { try { while (bConnected) { String str = dis.readUTF(); System.out.println(str); for (int i = 0; i < clients.size(); i++) { Client c = clients.get(i); c.send(str); // System.out.println(" a string send !"); } /* * for(Iterator<Client> it = clients.iterator(); * it.hasNext(); ) { Client c = it.next(); c.send(str); } */ /* * Iterator<Client> it = clients.iterator(); * while(it.hasNext()) { Client c = it.next(); c.send(str); * } */ } } catch (EOFException e) { System.out.println("Client closed!"); } catch (IOException e) { e.printStackTrace(); } finally { try { System.out.println("close All !"); if (dis != null) dis.close(); if (dos != null) dos.close(); if (s != null) { s.close(); // s = null; } } catch (IOException e1) { e1.printStackTrace(); } } } } }</span>
接下来先运行服务器代码,再运行手机端就可以了,多台手机可以互相发送信息了。
晚点再写UDP的
时间: 2024-10-27 11:11:55