您的位置:首页 > 移动开发 > Android开发

Android跨进程通信之Socket

2018-01-29 22:27 197 查看
借鉴自开发艺术

Socket,套接字,分为流式套接字TCP,用户数据报套接字UDP。

TCP面向连接,稳定,双向,有着经典的3次挥手、4次挥手,有超时重传机制

UDP面向地址,不稳定,拥塞、复杂网络环境可能会丢失,但效率更高,双向

权限

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>


先在远程Service建立一个TCP服务,再在主界面连接TCP服务,再在服务端进行一定的处理,使得可以接受多个客户端的连接。

这里监听的是8688端口。当有客户端连接的时候,会生成一个新的Socket(如果是真正的服务器,只有一个套接字)

当客户端断开连接时,服务端这边也会关闭对应Socket。这里的实现原理是通过判断服务端输入流的返回值来判断。当客户端断开连接时,服务端的输入流会返回null。

服务端

public class TCPServerService extends Service {

private static final String TAG = "xbh";

private boolean mIsServiceDestoryed = false;

private String[] mDefinedMessages = new String[]{
"hello",
"hi",
"good",
"perfect",
"pretty"
};

@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}

public TCPServerService() {
}

@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onDestroy() {
mIsServiceDestoryed = true;
super.onDestroy();
}

private class TcpServer implements Runnable {

@Override
public void run() {
ServerSocket serverSocket = null;//视为端口
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
Log.e(TAG, "finished, 8688");
e.printStackTrace();
return;
}

while (!mIsServiceDestoryed) {
//正式进入提供服务状态
try {
//接受到连接
final Socket client = serverSocket.accept();
Log.i(TAG, "accept");
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()));//获取客户端输入流
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
Log.i(TAG, "Welcome");
while (!mIsServiceDestoryed) {
String str = in.readLine();
Log.i(TAG, str);
if (str == null) {
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.println(msg);//服务端返回的消息 写入输出流
Log.i(TAG, msg);
}
Log.i(TAG, "Quit");
out.close();
in.close();
client.close();
}
}


客户端代码

public class TCPClientActivity extends AppCompatActivity implements View.OnClickListener {

private static final String TAG = "xbh";

private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;

private Button mSendButton;
private TextView mMessageTextView;
private EditText mMessageEditText;

private PrintWriter mPrintWriter;
private Socket mClientSocket;

private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_RECEIVE_NEW_MSG: {
mMessageTextView.setText(mMessageTextView.getText() + (String) msg.obj);
break;
}
case MESSAGE_SOCKET_CONNECTED: {
mSendButton.setEnabled(true);
break;
}
}
return false;
}
});

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcpclient);
mMessageTextView = findViewById(R.id.msg_container);
mSendButton = findViewById(R.id.send);
mSendButton.setOnClickListener(this);
mMessageEditText = findViewById(R.id.msg);
Intent service = new Intent(this, TCPServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}

@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}

@Override
public void onClick(View v) {
if (v == mSendButton) {
final String msg = mMessageEditText.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
mPrintWriter.println(msg);
mMessageEditText.setText("");
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "self" + time + ":" + msg + "\n";
mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
}
}
}

@SuppressLint("SimpleDateFormat")
private String formatDateTime(long time) {
return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}

private void connectTCPServer() {
Socket socket = null;
//分别从socket中取出了输出流和输入流
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);//代表连接上了
Log.i(TAG, "connect success");
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "connect failed "+e.getMessage());
}
}

BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

while (!TCPClientActivity.this.isFinishing()) {
String msg = br.readLine();
Log.i(TAG, msg);
if (msg != null) {
String time = formatDateTime(System.currentTimeMillis());
final String shoeMsg = "server" + time + ":" + msg + "\n";
//Handler的作用仅仅是切换线程,更新UI 整个逻辑很简单,不断从输入流和输出流中取。这个方法运行在子线程中,所以不会ANR
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, shoeMsg).sendToTarget();
}
}
Log.i(TAG, "Quit");
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: