socket是通過(guò)網(wǎng)絡(luò)來(lái)進(jìn)行進(jìn)程間通信的牺勾。socket也稱為“套接字”阵漏,是網(wǎng)絡(luò)通信中的概念,分為流式套接字和用戶數(shù)據(jù)報(bào)套接字兩種川无,分別對(duì)應(yīng)網(wǎng)絡(luò)傳輸控制中TCP和UDP協(xié)議懦趋。TCP協(xié)議是面向連接的協(xié)議疹味,提供穩(wěn)定的雙向通信功能糙捺,TCP連接的建立需要經(jīng)過(guò)“經(jīng)過(guò)三次握手”洪灯,還提供了超時(shí)重傳的機(jī)制,具有很高的穩(wěn)定性掏呼。而UDP是無(wú)連接的憎夷,提供不穩(wěn)定的單身通信功能拾给,UDP也可以實(shí)現(xiàn)雙向通信功能。在性能上级及,UDP具有更好的效率额衙,其缺點(diǎn)是不保證數(shù)據(jù)能夠正確傳輸,尤其在網(wǎng)絡(luò)擁塞的情況入偷。
接下來(lái)介紹的是一個(gè)聊天程序,兩個(gè)進(jìn)程可以通過(guò)socket來(lái)實(shí)現(xiàn)信息的傳輸殿雪,socket本身可以傳遞任意字節(jié)流丙曙,這里僅傳輸文本亏镰,很顯然這是一種IPC方式索抓。
首先要聲明權(quán)限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
建立一個(gè)遠(yuǎn)程TCP服務(wù):
public class TCPServerService extends Service {
private boolean isServiceDestoryed = false;
private String[] mDefinedMessages = new String[]{"你好", "what's you name?", "我很聰明"};
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
isServiceDestoryed = true;
super.onDestroy();
}
private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
System.err.println("建立tcp服務(wù)失敗,port:8688");
e.printStackTrace();
return;
}
/**
* 此處循環(huán)接收客戶端請(qǐng)求(有客戶端連接就可做出反應(yīng)篮幢,可同時(shí)和多個(gè)客戶端連接)
* 每次有客戶端連接就會(huì)生成一個(gè)新的socket
*/
while (!isServiceDestoryed) {
try {
//接收客戶端請(qǐng)求
final Socket client = serverSocket.accept();
System.out.println("accepted");
new Thread() {
@Override
public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException {
//用于接收客戶端消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
//用于向客戶端發(fā)送消息
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())), true);
out.print("welcome!");
while (!isServiceDestoryed) {
String str = in.readLine();
System.out.println("msg from client:" + str);
if (str == null) {
//客戶端斷開(kāi)連接
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.print(msg);
System.out.println("send :" + msg);
}
System.out.println("client out");
//關(guān)閉流
out.close();
in.close();
client.close();
}
}
接下來(lái)是客戶端Activity,在onCreate里開(kāi)戶一個(gè)線程去連接服務(wù)端socket搜锰,為了能夠確定連接成功纽乱,這里采用了超時(shí)重連的策略,為了降低重連的開(kāi)銷加入了休眠機(jī)制sleep薯嗤。
package com.example.administrator.myapplication.socket;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.TextView;
import com.example.administrator.myapplication.R;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class SocketActivity extends AppCompatActivity {
//接收到了消息
private static final int MSG_RECEIVE = 1;
//連接服務(wù)端
private static final int MSG_CONNECTED = 2;
private TextView msgTextView;
private AutoCompleteTextView input;
private Button sendBtn;
private PrintWriter printWriter;
private Socket mClientSocket;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RECEIVE:
msgTextView.setText(msgTextView.getText() + "\n" + String.valueOf(msg.obj));
break;
case MSG_CONNECTED:
sendBtn.setEnabled(true);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
msgTextView = (TextView) findViewById(R.id.textView);
input = (AutoCompleteTextView) findViewById(R.id.input);
sendBtn = (Button) findViewById(R.id.button);
Intent service = new Intent(this, TCPServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}
private void connectTCPServer() {
Socket socket = null;
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
handler.sendEmptyMessage(MSG_CONNECTED);
System.out.println("連接成功捏题!");
} catch (IOException e) {
e.printStackTrace();
SystemClock.sleep(1000);
System.out.println("連接失敶洹窟社!重試中绪钥。匣吊。寸潦。");
}
}
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!SocketActivity.this.isFinishing()){//當(dāng)前activity沒(méi)有關(guān)閉
String msg = bufferedReader.readLine();
System.out.println("接收:" + msg);
if (msg != null) {
handler.obtainMessage(MSG_RECEIVE, msg).sendToTarget();
}
}
System.out.println("quit...");
printWriter.close();
bufferedReader.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void onClick(View view) {
int id = view.getId();
switch (id){
case R.id.button:
final String msg = input.getText().toString();
if(!TextUtils.isEmpty(msg) && printWriter != null){
printWriter.println(msg);
input.setText("");
msgTextView.setText(msgTextView.getText() + "\n" + String.valueOf(msg));
}
}
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
注意:socket不用時(shí)記得關(guān)閉!S酱啤赊抖!
這里客戶端成功連接服務(wù)端后氛雪,用while循環(huán)來(lái)不斷去讀取服務(wù)端發(fā)來(lái)的消息报亩,只有當(dāng)activity退出時(shí)弦追,退出循環(huán)并關(guān)閉socket劲件。
參考:android開(kāi)發(fā)藝術(shù)探索