即時通訊系列閱讀
1. 即時通訊簡介
即時通訊(Instant Messaging)是目前Internet 上最為流行的通訊方式,各種各樣的即時通訊軟件也層出不窮;服務提供商也提供了越來越豐富的通訊服務功能室抽。不容置疑览祖,Internet 已經(jīng)成為真正的信息高速公路。從實際工程應用角度出發(fā),以計算機網(wǎng)絡原理為指導,結(jié)合當前網(wǎng)絡中的一些常用技術,編程實現(xiàn)基于C/S 架構的網(wǎng)絡聊天工具是切實可行的扭仁。
目前垮衷,中國市場上的企業(yè)級即時通信工具主要包括:信鴿、視高科技的視高可視協(xié)同辦公平臺乖坠、263EM搀突、群英CC2010、通軟聯(lián)合的GoCom熊泵、騰訊公司的RTX仰迁、IBM 的Lotus Sametime、點擊科技的GKE顽分、中國互聯(lián)網(wǎng)辦公
室的imo徐许、中國移動的企業(yè)飛信、華夏易聯(lián)的e-Link卒蘸、擎旗的UcStar 等雌隅。相對于個人即時通信工具而言,企業(yè)級即時通信工具更加強調(diào)安全性、實用性澄步、穩(wěn)定性和擴展性冰蘑。
1.1 即時聊天的解決方案
- socket:套接字,連接需要
ip
和端口
村缸,分為tcp和udp兩種形式 - xmpp:xmpp + openfire + asmack
1.2 常見協(xié)議
1.3 常見的術語
- xmpp:基于xml的可拓展協(xié)議.
- jabber:xmpp的前身.
- openfire:支持xmpp的開源服務器
- smack.jar:對xmpp協(xié)議封裝.方便開發(fā)的jar包.
- spark.exe:基于xmpp的pc客戶端;
- asmack.jar:smack.jar的精簡版.專門針對android端開發(fā)
2. 基本概念和原理
2.1 常用的網(wǎng)絡通信協(xié)議
TCP/IP:Transmission Control Protocol/Internet Protocol 的簡寫祠肥,中譯名為傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議,又名網(wǎng)絡通訊協(xié)議梯皿,是Internet 最基本的協(xié)議仇箱、Internet 國際互聯(lián)網(wǎng)絡的基礎,由網(wǎng)絡層的IP 協(xié)議和傳輸層的TCP協(xié)議組成东羹。TCP/IP 定義了電子設備如何連入因特網(wǎng)剂桥,以及數(shù)據(jù)如何在它們之間傳輸?shù)臉藴省f(xié)議采用了4 層的層級結(jié)構属提,每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求权逗。通俗而言:TCP 負責發(fā)現(xiàn)傳輸?shù)膯栴},一有問題就發(fā)出信號冤议,要求重新傳輸斟薇,直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡亍6鳬P 是給因特網(wǎng)的每一臺聯(lián)網(wǎng)設備規(guī)定一個地址恕酸。
UDP:UDP 協(xié)議全稱是用戶數(shù)據(jù)報協(xié)議堪滨,在網(wǎng)絡中它與TCP 協(xié)議一樣用于處理數(shù)據(jù)包点弯,是一種無連接的協(xié)議蒲犬。在OSI 模型中搬设,在第四層——傳輸層稿存,處于IP 協(xié)議的上一層涎拉。UDP 有不提供數(shù)據(jù)包分組赊级、組裝和不能對數(shù)據(jù)包進行排序的缺點陈轿,也就是說吝羞,當報文發(fā)送之后凉翻,是無法得知其是否安全完整到達的筐咧。UDP 用來支持那些需要在計算機之間傳輸數(shù)據(jù)的網(wǎng)絡應用。包括網(wǎng)絡視頻會議系統(tǒng)在內(nèi)的眾多的客戶/服務器模式的網(wǎng)絡應用都需要使用UDP協(xié)議噪矛。UDP 協(xié)議從問世至今已經(jīng)被使用了很多年量蕊,雖然其最初的光彩已經(jīng)被一些類似協(xié)議所掩蓋,但是即使是在今天UDP 仍然不失為一項非常實用和可行的網(wǎng)絡傳輸層協(xié)議艇挨。
TCP/IP 協(xié)議棧主要分為四層:應用層残炮、傳輸層、網(wǎng)絡層缩滨、數(shù)據(jù)鏈路層,每層都有相應的協(xié)議势就,如下圖:
所謂的協(xié)議就是雙方進行數(shù)據(jù)傳輸?shù)囊环N格式泉瞻。
2.2 TCP、UDP 特點對比
TCP 協(xié)議是面向連接苞冯、保證高可靠性(數(shù)據(jù)無丟失袖牙、數(shù)據(jù)無失序、數(shù)據(jù)無錯誤舅锄、數(shù)據(jù)無重復到達)傳輸層協(xié)議鞭达。UDP 協(xié)議也是傳輸層協(xié)議,它是無連接皇忿,不保證可靠的傳輸層協(xié)議畴蹭。
2.3 TCP 三次握手過程
1、請求端(通常稱為客戶)發(fā)送一個SYN 段指明客戶打算連接的服務器的端口鳍烁,以及初始序號(ISN)
2叨襟、服務器發(fā)回包含服務器的初始序號的SYN 報文段(報文段2)作為應答。同時幔荒,將確認序號設置為客戶的ISN加1 以對客戶的SYN 報文段進行確認糊闽。
TCP | UDP |
---|---|
面向連接 | 面向非連接 |
可靠的連接 | 不可靠的連接 |
速度慢 | 速度快 |
大文件、重要的數(shù)據(jù)等 | 適合小數(shù)據(jù)爹梁、不重要 |
3右犹、客戶必須將確認序號設置為服務器的ISN 加1 以對服務器的SYN 報文段進行確認(報文段3)這三個報文段完成連接的建立。這個過程也稱為三次握手(three-way handshake)卫键。
上面的過程如下圖所示:
2.4 即時通訊形式
直接通訊
兩個不同客戶端之間不經(jīng)過服務器傀履,直接通過網(wǎng)絡進行數(shù)據(jù)的交互虱朵。常用的p2p 技術就是直接通訊的形式莉炉。
在線代理通訊
一個客戶端發(fā)送的消息先發(fā)送到服務器,服務器接收到消息后再發(fā)送給指定的另外一個客戶端碴犬。QQ 的消息
尤其是離線消息就是同在線代理的方式實現(xiàn)的絮宁。
離線代理通訊
一個客戶端發(fā)送消息給服務器,服務器存儲在數(shù)據(jù)庫中個服协,當另外一個客戶端上線后在發(fā)送過去绍昂。
離線擴展通訊
一個客戶端發(fā)送消息給服務器,服務器通過郵件偿荷、短信等其他形式將消息發(fā)送給接收者窘游。
3. ServerSocket 和Socket
3.1 使用Java 完成簡單的Socket 通信
在Java 中Socket 可以理解為客戶端或者服務器端的一個特殊的對象,這個對象有兩個關鍵的方法跳纳,一個是getInputStream 方法忍饰,另一個是getOutputStream 方法。getInputStream 方法可以得到一個輸入流寺庄,客戶端的Socket對象上的getInputStream 方法得到的輸入流其實就是從服務器端發(fā)回的數(shù)據(jù)流艾蓝。GetOutputStream 方法得到一個輸出流力崇,客戶端Socket 對象上的getOutputStream 方法返回的輸出流就是將要發(fā)送到服務器端的數(shù)據(jù)流,(其實是一個緩沖區(qū)赢织,暫時存儲將要發(fā)送過去的數(shù)據(jù))亮靴。
下面就讓我們寫一個簡單的Demo 來演示Socket 是如何使用的。
建立服務器類
服務類使用到的核心類的是ServerSocket于置。這里我們只需要建立一個Java Project 即可茧吊。
public class IMServer {
private static ServerSocket serverSocket;
private static BufferedReader reader;
public static void main(String[] args) {
try {
serverSocket = new ServerSocket(7788);
/**
* 等待接收客戶端連接進來,該方法是線程阻塞的
*/
Socket accept = serverSocket.accept();
/**
* 獲取輸入流俱两,用于接收客戶端發(fā)來的數(shù)據(jù)
*/
InputStream inputStream = accept.getInputStream();
/**
* 將字節(jié)輸入流轉(zhuǎn)化為字符輸出流
*/
reader = new BufferedReader(new InputStreamReader(inputStream));
/**
* 打印數(shù)據(jù)
*/
String tmp = null;
while ((tmp = reader.readLine()) != null) {
System.out.println(tmp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
建立客戶端類
public class IMClient {
private static Socket socket;
private static BufferedWriter writer;
/**
* @param args
*/
public static void main(String[] args) {
try {
socket = new Socket("127.0.0.1", 7788);
/**
* 獲取輸出流
*/
OutputStream outputStream = socket.getOutputStream();
writer = new BufferedWriter(new OutputStreamWriter(outputStream));
writer.write("hello wo shi wzy!" + new Date().getTime());
writer.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在上面的代碼中我們僅僅實現(xiàn)了一個最簡單的服務器和客戶端饱狂,服務器啟動起來后只能接受到一次消息,然后就關閉了宪彩。如果想讓服務器一直運行休讳,應該通過死循環(huán)來處理不同的發(fā)送進來的消息。
Socket調(diào)試工具
TCP/UDP Socket調(diào)試工具提供了TCP Server,TCP Client,UDP Server,UDP Client,UDP Group 五種Socket調(diào)試方案
TCP
手機作為Client尿孔,PC作為Server
public class MyClientActivity extends Activity {
private EditText mEditText = null;
private Button connectButton = null;
private Button sendButton = null;
private TextView mTextView = null;
private Socket clientSocket = null;
private OutputStream outStream = null;
private Handler mHandler = null;
private ReceiveThread mReceiveThread = null;
private boolean stop = true;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEditText = (EditText) this.findViewById(R.id.edittext);
mTextView = (TextView) this.findViewById(R.id.retextview);
connectButton = (Button) this.findViewById(R.id.connectbutton);
sendButton = (Button) this.findViewById(R.id.sendbutton);
sendButton.setEnabled(false);
// 連接按鈕監(jiān)聽
connectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
// 實例化對象并連接到服務器
clientSocket = new Socket("172.27.35.1", 60000);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
displayToast("連接成功俊柔!");
// 連接按鈕使能
connectButton.setEnabled(false);
// 發(fā)送按鈕使能
sendButton.setEnabled(true);
mReceiveThread = new ReceiveThread(clientSocket);
stop = false;
// 開啟線程
mReceiveThread.start();
}
});
// 發(fā)送數(shù)據(jù)按鈕監(jiān)聽
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
byte[] msgBuffer = null;
// 獲得EditTex的內(nèi)容
String text = mEditText.getText().toString();
try {
// 字符編碼轉(zhuǎn)換
msgBuffer = text.getBytes("GB2312");
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
// 獲得Socket的輸出流
outStream = clientSocket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
// 發(fā)送數(shù)據(jù)
outStream.write(msgBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 清空內(nèi)容
mEditText.setText("");
displayToast("發(fā)送成功!");
}
});
// 消息處理
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 顯示接收到的內(nèi)容
mTextView.setText((msg.obj).toString());
}
};
}
// 顯示Toast函數(shù)
private void displayToast(String s) {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
private class ReceiveThread extends Thread {
private InputStream inStream = null;
private byte[] buf;
private String str = null;
ReceiveThread(Socket s) {
try {
// 獲得輸入流
this.inStream = s.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
while (!stop) {
this.buf = new byte[512];
try {
// 讀取輸入數(shù)據(jù)(阻塞)
this.inStream.read(this.buf);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 字符編碼轉(zhuǎn)換
try {
this.str = new String(this.buf, "GB2312").trim();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg = new Message();
msg.obj = this.str;
// 發(fā)送消息
mHandler.sendMessage(msg);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mReceiveThread != null) {
stop = true;
mReceiveThread.interrupt();
}
}
}
手機作為Server活合,PC作為Client
public class MyServerActivity extends Activity {
private TextView ipTextView = null;
private EditText mEditText = null;
private Button sendButton = null;
private TextView mTextView = null;
private OutputStream outStream = null;
private Socket clientSocket = null;
private ServerSocket mServerSocket = null;
private Handler mHandler = null;
private AcceptThread mAcceptThread = null;
private ReceiveThread mReceiveThread = null;
private boolean stop = true;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ipTextView = (TextView) this.findViewById(R.id.iptextview);
mEditText = (EditText) this.findViewById(R.id.sedittext);
sendButton = (Button) this.findViewById(R.id.sendbutton);
sendButton.setEnabled(false);
mTextView = (TextView) this.findViewById(R.id.textview);
// 發(fā)送數(shù)據(jù)按鈕監(jiān)聽
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
byte[] msgBuffer = null;
// 獲得EditTex的內(nèi)容
String text = mEditText.getText().toString();
try {
// 字符編碼轉(zhuǎn)換
msgBuffer = text.getBytes("GB2312");
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
// 獲得Socket的輸出流
outStream = clientSocket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
// 發(fā)送數(shù)據(jù)
outStream.write(msgBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 清空內(nèi)容
mEditText.setText("");
displayToast("發(fā)送成功雏婶!");
}
});
// 消息處理
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0: {
// 顯示客戶端IP
ipTextView.setText((msg.obj).toString());
// 使能發(fā)送按鈕
sendButton.setEnabled(true);
break;
}
case 1: {
// 顯示接收到的數(shù)據(jù)
mTextView.setText((msg.obj).toString());
break;
}
}
}
};
mAcceptThread = new AcceptThread();
// 開啟監(jiān)聽線程
mAcceptThread.start();
}
// 顯示Toast函數(shù)
private void displayToast(String s) {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
private class AcceptThread extends Thread {
@Override
public void run() {
try {
// 實例化ServerSocket對象并設置端口號為7100
mServerSocket = new ServerSocket(60000);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
// 等待客戶端的連接(阻塞)
clientSocket = mServerSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mReceiveThread = new ReceiveThread(clientSocket);
stop = false;
// 開啟接收線程
mReceiveThread.start();
Message msg = new Message();
msg.what = 0;
// 獲取客戶端IP
msg.obj = clientSocket.getInetAddress().getHostAddress();
// 發(fā)送消息
mHandler.sendMessage(msg);
}
}
private class ReceiveThread extends Thread {
private InputStream mInputStream = null;
private byte[] buf;
private String str = null;
ReceiveThread(Socket s) {
try {
// 獲得輸入流
this.mInputStream = s.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
while (!stop) {
this.buf = new byte[512];
// 讀取輸入的數(shù)據(jù)(阻塞讀)
try {
this.mInputStream.read(buf);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 字符編碼轉(zhuǎn)換
try {
this.str = new String(this.buf, "GB2312").trim();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg = new Message();
msg.what = 1;
msg.obj = this.str;
// 發(fā)送消息
mHandler.sendMessage(msg);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mReceiveThread != null) {
stop = true;
mReceiveThread.interrupt();
}
}
}
UDP
public class MainActivity extends Activity {
private static String TAG = "CallActivity";
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
// 播放聲音
initBeepSound();
playBeepSoundAndVibrate();
Log.i(TAG, "reciever_msg");
String result = (String) msg.obj;
System.out.println(result);
tv_result.setText(result);
};
};
private String ip;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ip = IpUtil.getIp(this);
Toast.makeText(this, ip, Toast.LENGTH_LONG).show();
System.out.println("ip:" + ip);
tv_result = (TextView) findViewById(R.id.tv_result);
}
/**
* 發(fā)送者
*/
public void send(View v) {
new Thread() {
@Override
public void run() {
try {
//1. 創(chuàng)建一個DatagramSocket對象
DatagramSocket socket = new DatagramSocket(5678);
//2. 創(chuàng)建一個 InetAddress , 相當于是地址,就是想要發(fā)送的ip地址
InetAddress serverAddress = InetAddress.getByName("172.27.35.1");
//3. 這是隨意發(fā)送一個數(shù)據(jù)
String str = "來自android手機的問候";
//4. 轉(zhuǎn)為byte類型
byte data[] = str.getBytes("GBK");
//5. 創(chuàng)建一個DatagramPacket 對象白指,并指定要講這個數(shù)據(jù)包發(fā)送到網(wǎng)絡當中的哪個地址留晚,以及端口號
DatagramPacket pack = new DatagramPacket(data, data.length, serverAddress, 5678);
//6. 調(diào)用DatagramSocket對象的send方法 發(fā)送數(shù)據(jù)
socket.send(pack);
} catch (SocketException e) {
Log.i(TAG, "error");
e.printStackTrace();
} catch (UnknownHostException e) {
Log.i(TAG, "error");
e.printStackTrace();
} catch (IOException e) {
Log.i(TAG, "error");
e.printStackTrace();
}
}
}.start();
}
/**
* 接收者
* @param v
*/
public void receive(View v) {
new Thread() {
@Override
public void run() {
// 執(zhí)行完畢后給handler發(fā)送一個空消息
try {
// 1. 創(chuàng)建一個DatagramSocket對象,并指定監(jiān)聽的端口號/\
DatagramSocket socket = new DatagramSocket(5678);
// 2. 創(chuàng)建一個byte數(shù)組用于接收
byte data[] = new byte[1024];
// 3. 創(chuàng)建一個空的DatagramPackage對象
DatagramPacket pack = new DatagramPacket(data, data.length);
// 4. 使用receive方法接收發(fā)送方所發(fā)送的數(shù)據(jù),同時這也是一個阻塞的方法
while (true) {
Log.i(TAG, "reciever_1");
socket.receive(pack);
Log.i(TAG, "reciever_2");
// 5. 得到發(fā)送過來的數(shù)據(jù)
// String result = new String(pack.getData(), pack.getOffset(), pack.getLength(),"GBK");
String result = new String(pack.getData(), pack.getOffset(), pack.getLength());
Message msg = new Message();
msg.obj = result;
handler.sendMessage(msg);
Log.i(TAG, "sendmsg_1");
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i(TAG, "error");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i(TAG, "error");
}
}
}.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
// 播放聲音
private static final float BEEP_VOLUME = 0.10f;
private MediaPlayer mediaPlayer;
private void initBeepSound() {
if (mediaPlayer == null) {
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(beepListener);
AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.beep);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
file.close();
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
mediaPlayer.prepare();
} catch (IOException e) {
mediaPlayer = null;
}
}
}
private void playBeepSoundAndVibrate() {
if (mediaPlayer != null) {
mediaPlayer.start();
}
// 震動
Vibrator mVibrator = (Vibrator) getApplication().getSystemService(Service.VIBRATOR_SERVICE);
mVibrator.vibrate(2000);
long[] pattern = { 0, 100, 200, 100, 200 };
mVibrator.vibrate(pattern, -1);
}
private final OnCompletionListener beepListener = new OnCompletionListener() {
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.seekTo(0);
}
};
// 退出提醒
private long exitTime;
private TextView tv_result;
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if ((System.currentTimeMillis() - exitTime) > 2000) {
Toast.makeText(getApplicationContext(), "再按一次退出" + getResources().getString(R.string.app_name),
Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
}
IpUtil.java
public class IpUtil {
/**
* 獲取手機ip
*
* @return
*/
public static String getLocalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
String ip = inetAddress.getHostAddress().toString();
System.out.println("getLocalIpAddressIP:"+ip);
return ip;
}
}
}
} catch (SocketException ex) {
Log.e("ifo", ex.toString());
}
return "";
}
public static String getIp(Activity activity) {
WifiManager wifiManager = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();
// 格式化IP address告嘲,例如:格式化前:1828825280错维,格式化后:192.168.1.109
String ip = String.format("%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff), (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));
System.out.println("getIpIP:"+ip);
return ip;
}
}