这几天学习了下在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>
接下来先运行服务器代码,再运行手机端就可以了,多台手机可以互相发送信息了。