Android Socket 专题:
UDP Client客户端 http://blog.csdn.net/shankezh/article/details/50731287
UDP Server服务器 http://blog.csdn.net/shankezh/article/details/51452811
TCP Client客户端 http://blog.csdn.net/shankezh/article/details/70763579
TCP Server服务器 http://blog.csdn.net/shankezh/article/details/51555455
这个章节补充UDP Server服务器的内容。
首先规划自己的界面,附上XML效果图:
附上Xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
android:layout_weight="2"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="2">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ip:"/>
<EditText
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
android:id="@+id/editIp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="port"/>
<EditText
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:id="@+id/editPort"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:id="@+id/btn_udpStart"
android:text="开启服务器" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:id="@+id/btn_udpClose"
android:text="关闭连接" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:id="@+id/btn_CleanRcv"
android:text="清除接收区" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:id="@+id/btn_CleanSend"
android:text="清除接收区" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:text="接收区:" />
<TextView
android:layout_width="match_parent"
android:layout_weight="7"
android:layout_height="0dp"
android:id="@+id/txt_Rcv"
android:background="@android:color/holo_blue_light"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:text="发送区:" />
<TextView
android:layout_width="match_parent"
android:layout_weight="7"
android:background="@android:color/holo_purple"
android:id="@+id/txt_Send"
android:layout_height="0dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="2">
<EditText
android:layout_width="0dp"
android:layout_weight="5"
android:id="@+id/edit_Send"
android:layout_height="match_parent" />
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_gravity="right"
android:id="@+id/btn_Send"
android:text="发送"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
界面布局完成,讲一点关键性的东西。
与UDP客户端一样的是,实现UDP服务器关键在于这几个类:
1、DatagramSocket
2、DatagramPacket
3、InetSocketAddress
需要各位自己搜索一下这几个类的意思,我就偷个懒。
Udp Server服务器的收发与UDP Client客户端如出一辙,实现思路如下:
1、创建DatagramSocket通信数据报
2、建立接收事件专用DatagramPacket数据包
3、创建超时(这个和后面关闭通信有关)
4、建立监听接收消息循环机制(接收消息处理在此处,接收到的消息通过BroadcastReceiver发送给主界面)
5、结束循环,关闭数据报。
Udp Server 服务器发送消息的函数实现思路如下:
1、共享一个DatagramSocket
2、从接收数据包那儿获取对方的ip和port
3、更新需要发送的内容到DatagramPacket
4、使用之前的DatagramSocket的发送方法将内容发出去
调用的时候直接在新线程里调用这个函数方法即可。
具体看代码实现:
package com.example.udpserver;
import android.content.Intent;
import android.util.Log;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/**
* Created by Jason on 2016/5/18.
*/
public class UdpServer implements Runnable {
private String ip = null;
private int port = 0;
private DatagramPacket dpRcv = null,dpSend = null;
private static DatagramSocket ds = null;
private InetSocketAddress inetSocketAddress = null;
private byte[] msgRcv = new byte[1024];
private boolean udpLife = true; //udp生命线程
private boolean udpLifeOver = true; //生命结束标志,false为结束
public UdpServer(String mIp, int mPort) {
this.ip = mIp;
this.port = mPort;
}
private void SetSoTime(int ms) throws SocketException {
ds.setSoTimeout(ms);
}
//返回udp生命线程因子是否存活
public boolean isUdpLife(){
if (udpLife){
return true;
}
return false;
}
//返回具体线程生命信息是否完结
public boolean getLifeMsg(){
return udpLifeOver;
}
//更改UDP生命线程因子
public void setUdpLife(boolean b){
udpLife = b;
}
public void Send(String sendStr) throws IOException {
Log.i("SocketInfo","客户端IP:"+ dpRcv.getAddress().getHostAddress() +"客户端Port:"+ dpRcv.getPort());
dpSend = new DatagramPacket(sendStr.getBytes(),sendStr.getBytes().length,dpRcv.getAddress(),dpRcv.getPort());
ds.send(dpSend);
}
@Override
public void run() {
inetSocketAddress = new InetSocketAddress(ip, port);
try {
ds = new DatagramSocket(inetSocketAddress);
Log.i("SocketInfo", "UDP服务器已经启动");
SetSoTime(3000);
//设置超时,不需要可以删除
} catch (SocketException e) {
e.printStackTrace();
}
dpRcv = new DatagramPacket(msgRcv, msgRcv.length);
while (udpLife) {
try {
Log.i("SocketInfo", "UDP监听中");
ds.receive(dpRcv);
String string = new String(dpRcv.getData(), dpRcv.getOffset(), dpRcv.getLength());
Log.i("SocketInfo", "收到信息:" + string);
Intent intent =new Intent();
intent.setAction("udpReceiver");
intent.putExtra("udpReceiver",string);
MainActivity.context.sendBroadcast(intent);//将消息发送给主界面
} catch (IOException e) {
e.printStackTrace();
}
}
ds.close();
Log.i("SocketInfo","UDP监听关闭");
//udp生命结束
udpLifeOver = false;
}
}
至此,核心代码部署完成,期间关于发送给主界面的,设置超时,不需要的可以删除。
下面主界面代码实现:
MyHandler类(主要处理UI更新事件)
OnCreate(绑定控件,事件监听,注册BroadcastReceiver等)
MyBtnClick类(处理各按钮事件)
MyBroadcastReceiver(广播接收器)
看具体代码:
package com.example.udpserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.io.IOException;
import java.lang.ref.WeakReference;
public class MainActivity extends AppCompatActivity {
private Button btnUdpStart, btnUdpClose,btnRcvClear,btnSendClear,btnUdpSend;
private TextView txtRcv,txtSend;
private EditText editSend,editIp,editPort;
public static Context context ;
private MyHandler myHandler = new MyHandler(this);
private MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
private MyBtnClick myBtnClick = new MyBtnClick();
private static UdpServer udpServer = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
BindWidget();
Listening();
BindReceiver();
}
private void BindWidget(){
btnUdpClose = (Button) findViewById(R.id.btn_udpClose);
btnUdpStart = (Button) findViewById(R.id.btn_udpStart);
btnUdpSend = (Button) findViewById(R.id.btn_Send);
btnRcvClear = (Button) findViewById(R.id.btn_CleanRcv);
btnSendClear= (Button) findViewById(R.id.btn_CleanSend);
txtRcv = (TextView) findViewById(R.id.txt_Rcv);
txtSend = (TextView) findViewById(R.id.txt_Send);
editIp = (EditText) findViewById(R.id.editIp);
editPort = (EditText) findViewById(R.id.editPort);
editSend = (EditText) findViewById(R.id.edit_Send);
}
private void Listening(){
btnUdpStart.setOnClickListener(myBtnClick);
btnUdpClose.setOnClickListener(myBtnClick);
btnUdpSend.setOnClickListener(myBtnClick);
btnRcvClear.setOnClickListener(myBtnClick);
btnSendClear.setOnClickListener(myBtnClick);
}
private void BindReceiver(){
IntentFilter intentFilter = new IntentFilter("udpReceiver");
registerReceiver(myBroadcastReceiver,intentFilter);
}
private class MyHandler extends Handler{
private final WeakReference<MainActivity> mActivity;
public MyHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivity.get();
if (null != activity){
switch (msg.what){
case 1:
String str = msg.obj.toString();
txtRcv.append(str);
break;
case 2:
String stra = msg.obj.toString();
txtSend.append(stra);
break;
case 3:
break;
}
}
}
}
private class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String mAction = intent.getAction();
switch (mAction){
case "udpReceiver":
String msg = intent.getStringExtra("udpReceiver");
Message message = new Message();
message.what = 1;
message.obj = msg;
myHandler.sendMessage(message);
break;
}
}
}
private class MyBtnClick implements Button.OnClickListener{
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_udpStart:
Log.i("asd","asdasd");
if (!editIp.getText().toString().isEmpty() && !editIp.getText().toString().isEmpty()){
int mPort = Integer.parseInt(editPort.getText().toString());
udpServer = new UdpServer(editIp.getText().toString(),mPort);
Thread thread = new Thread(udpServer);
thread.start();
btnUdpStart.setEnabled(false);
btnUdpClose.setEnabled(true);
}else {
Log.i("DebugInfo","请输入Ip或者Port");
}
break;
case R.id.btn_udpClose:
btnUdpClose.setEnabled(false);
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//关闭UDP
udpServer.setUdpLife(false);
while (udpServer.getLifeMsg()); //等待udp阻塞结束,这里就体现出超时的好处了
Looper.getMainLooper();
btnUdpStart.setEnabled(true);
}
});
thread.start();
break;
case R.id.btn_CleanRcv:
txtRcv.setText("");
break;
case R.id.btn_CleanSend:
txtSend.setText("");
break;
case R.id.btn_Send:
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
if (editSend.getText().toString() != null){
try {
udpServer.Send(editSend.getText().toString());
Message message = new Message();
message.what =2 ;
message.obj = editSend.getText().toString();
myHandler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}else {
Log.i("DebugInfo","请输入发送内容");
}
}
});
thread1.start();
break;
}
}
}
}
最后,我们在AndroidManifest文家中,加上权限声明即可:
<uses-permission android:name="android.permission.INTERNET"/>
写在这就全部结束了,和上次写的UDP Client做一个通信验证,实验如下图:
上图为UDP Server发送 im server内容,接收到了客户端发来的 im clietn内容显示在接收区
上图为UDPClent 发送im client内容,接收到服务器发送的im server内容。
udp服务端和客户端都验证通过。
附上本期Demo地址: