Unix系統中支持進程間通信(IPC),IPC的接口設計得類似文件IO操作接口。在Unix中,一個進程會有一套可以進行讀取寫入的IO描述符。IO描述符可以說是文件、設備或者是通信通道(socket套接字)。一個文件描述符由三部分組成:創建(打開socket)、讀取寫入數據(接收和發送到socket)、銷燬(關閉socket)。
消息的目的地址是使用socket地址來表示,一個socket地址是由網絡地址和端口號組成的通信標識符。
進程間通信操作需要客戶端和服務器端分別有一個socket對象。當一個消息發出後,這個消息在發送端的socket中處於排隊狀態,直到下層的網絡協議將這些消息發送出去;當消息到達接收端的socket後,也會處於排隊狀態,直到接收端的進程對這條消息進行了接收處理。
對於socket編程可以有兩種通信協議可以選擇:數據報通信和流通信。數據報通信就是UDP,UDP是一種無連接的協議,這意味着我們每次發送數據報時,需要同時發送本機的socket描述符和接收端的描述符。流通信即TCP,TCP是一種基於連接的協議,在通信之前,必須在通信的一對兒socket之間建立連接,其中的一個socket作爲服務器進行監聽請求,另一個作爲客戶端進行連接請求,一旦兩個socket建立好了連接,他們可以單向或者雙向進行數據傳輸。
TCP還是UDP的選擇。
UDP報頭有64KB的限制,發送的數據不一定會被接收端按順序接收;而TCP一旦socket建立了連接,它們之間的通信如同IO流,沒有大小限制,接收端收到的包和發送端的順序一致。因此TCP適合遠程登錄和文件傳輸這類的網絡服務,因爲這些需要傳輸的數據大小不確定。UDP相比TCP輕量一些,用於實現實時性較高或者丟包不重要的一些服務,例如微信消息的接收發送,在局域網內,UDP的丟包率相對較低。
一、基於UDP的Socket通信
客戶端
客戶端通過將數據放到byte數組中:
byte[] Data = “你想要發送的文本”.getBytes();
並通過一個數據包對象將數據封裝:
DatagramPacket packetS = new DatagramPacket(Data,Data.length,address,port);
最後通過DataGramSocket對象將該數據包發送:
client = new DatagramSocket();
client.send(packetS);
(1)設置網絡連接權限
在manifest文件的application前添加網絡訪問權限:
<uses-permission android:name="android.permission.INTERNET" />
(2)客戶端代碼
package com.ipc.lijiao.udpcilent;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class MainActivity extends AppCompatActivity {
EditText et;
Button sd;
int port = 8800;
String host = "127.0.0.1";
DatagramSocket client;
Handler mhandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et = (EditText)findViewById(R.id.et);
sd = (Button)findViewById(R.id.sd);
sd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = et.getText().toString();
Send send = new Send(content);
send.start();
}
});
}
public class Send extends Thread{
String content;
InetAddress address;
private Send(String content)
{
this.content = content;
}
public void run()
{
try {
address = InetAddress.getByName(host);
} catch (UnknownHostException e) {
e.printStackTrace();
}
byte[] Data = content.getBytes();
DatagramPacket packetS = new DatagramPacket(Data,Data.length,address,port);
try {
client = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
}
try {
client.send(packetS);
} catch (IOException e) {
e.printStackTrace();
}
byte[] DataSend = new byte[300];
/**
DatagramPacket 需要新建對象,創建方式如果爲packetS = new DatagramPacket(DataSend,DataSend.length);packetS仍然會指向原對象。
**/
DatagramPacket packetR = new DatagramPacket(DataSend,DataSend.length);
try {
client.receive(packetR);
} catch (IOException e) {
e.printStackTrace();
}
content = new String(packetR.getData(),0,packetR.getLength());
mhandler.post(new Runnable() {
@Override
public void run() {
et.setText(content);
}
});
client.close();
}
}
}
服務端
(2)服務器端
服務器端首先需要設置一個數據接收容器:
byte[] Data = new byte[300];
設置接收數據包:
DatagramPacket packetR = new DatagramPacket(Data,Data.length);
通過DataGramSocket對象接收數據到DatagramPacket對象中:
server = new DatagramSocket(8800);
server.receive(packetR);
讀取數據:
content = new String(packetR.getData(),0,packetR.getLength());
(1)設置網絡連接權限
在manifest文件的application前添加網絡訪問權限:
<uses-permission android:name="android.permission.INTERNET" />
(2)服務端代碼
package com.ipc.lijiao.udpserver;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class MainActivity extends AppCompatActivity {
int Max_SIZE = 300;
TextView show;
Handler mhandler;
String content;
DatagramSocket server;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (TextView)findViewById(R.id.show);
mhandler = new Handler();
Server ImpServer = new Server();
Thread server = new Thread(ImpServer);
server.start();
}
public class Server implements Runnable{
@Override
public void run() {
byte[] Data = new byte[Max_SIZE];
DatagramPacket packetR = new DatagramPacket(Data,Data.length);
try {
server = new DatagramSocket(8800);
} catch (SocketException e) {
e.printStackTrace();
}
while (true)
{
try {
server.receive(packetR);
} catch (IOException e) {
e.printStackTrace();
}
content = new String(packetR.getData(),0,packetR.getLength());
mhandler.post(new Runnable() {
@Override
public void run() {
show.setText(content);
}
});
byte[] DataS = ("Servier:"+content).getBytes();
DatagramPacket packetSend = new DatagramPacket(DataS,DataS.length,packetR.getAddress(),packetR.getPort());
try {
server.send(packetSend);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void onDestroy(){
super.onDestroy();
server.close();
}
}
二、基於TCP的Socket通信
客戶端
(1)基本思路
客戶端通過服務端的host和port創建一個Socket對象:
client = new Socket(host,port);
向服務端發送消息使用BufferedWriter對象:
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
client.getOutputStream()));
writer.write(show.getText().toString());
writer.flush();
client.shutdownOutput();
使用client.shutdownOutput()單向關閉寫入流(如果不關閉寫入流,則數據不會真正寫到服務端),如果使用writer.close()會將socket對象一併關閉。
(2)demon
package com.ipc.lijiao.ipcclient;
import android.os.Handler;
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 java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
EditText show ;
Button socketB ;
Handler mhandler = new Handler();
Socket client;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (EditText)findViewById(R.id.show);
socketB = (Button)findViewById(R.id.socketB);
socketB.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId())
{
case R.id.socketB:
{
new Thread()
{
public void run()
{
// String host = "172.17.100.77";
String host = "127.0.0.1";
int port = 8919;
try
{
client = new Socket(host,port);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
writer.write(show.getText().toString());
writer.flush();
client.shutdownOutput();
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
String content = "";
while(true)
{
String line = null;
line = reader.readLine();
if(line == null)
break;
content = content + line;
}
client.shutdownInput();
final String context = content;
mhandler.post(new Runnable() {
@Override
public void run() {
show.setText(context);
}
});
client.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
}.start();
}
}
}
public void onDestroy() {
super.onDestroy();
mhandler.removeCallbacksAndMessages(null);
mhandler = null;
}
}
服務端
(1)基本思路
服務端會創建ServerSocket對象,並通過該對象的accept()方法監聽端口:
ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
socket對象通過BufferReader來接收信息,並通過ReadLine讀取信息:
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true)
{
String line = null;
line = reader.readLine();
if(line == null)
break;
}
(2)Demon
package com.ipc.lijiao.ipcserver;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import javax.security.auth.Destroyable;
public class MainActivity extends AppCompatActivity {
TextView show;
Handler mhandler = new Handler();
ServerSocket server;
Socket socket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (TextView)findViewById(R.id.textView);
AcceptClient();
}
private void AcceptClient()
{
new Thread()
{
@Override
public void run()
{
try
{
int port = 8919;
server = new ServerSocket(port);
while(true)
{
socket = server.accept();
String content = "";
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true)
{
String line = null;
line = reader.readLine();//讀取客戶端傳來的數據
if (line == null)
break;
content = content + line;
}
socket.shutdownInput();
final String context = content;
mhandler.post(new Runnable() {
@Override
public void run() {
show.setText(context);
}
});
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("客戶端發送到服務端的消息爲:"+context);
writer.flush();
socket.shutdownOutput();
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}.start();
}
public void onDestroy() {
super.onDestroy();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
mhandler.removeCallbacksAndMessages(null);
mhandler = null;
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}