引言
Socket是位于應(yīng)用層和傳輸層之間的一個(gè)抽象層,把TCP/IP層復(fù)雜的操作抽象為幾個(gè)簡(jiǎn)單的接口懊纳,供應(yīng)用層調(diào)用以實(shí)現(xiàn)進(jìn)程在網(wǎng)絡(luò)中通信漠趁。
Socket分為流式套接字和數(shù)據(jù)包套接字慕淡,分別對(duì)應(yīng)網(wǎng)絡(luò)傳輸控制層的TCP協(xié)議和UDP協(xié)議。TCP協(xié)議是一種面向連接的病附,可靠的问窃,基于字節(jié)流的傳輸層通信協(xié)議,它使用三次握手協(xié)議建立連接完沪,并且提供了超時(shí)重傳機(jī)制域庇,具有很高的穩(wěn)定性嵌戈。UDP協(xié)議是一種無連接的協(xié)議,且不對(duì)數(shù)據(jù)包進(jìn)行可靠性保證听皿。
在網(wǎng)絡(luò)差的情況下熟呛,UDP協(xié)議數(shù)據(jù)包丟失會(huì)比較嚴(yán)重,但由于其不屬于連接型協(xié)議尉姨,具有資源消耗少庵朝,處理速度快的優(yōu)點(diǎn),在音頻視頻等傳輸時(shí)使用UDP協(xié)議較多又厉。
示例
這里我們通過socket實(shí)現(xiàn)兩個(gè)進(jìn)程之間的通信九府。
- 建立兩個(gè)工程,分別是TestServerSocket和TestClientSocket覆致,并分別增加權(quán)限侄旬。
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- TestServerSocket
當(dāng)server service啟動(dòng)的時(shí)候,創(chuàng)建ServerSocket煌妈,對(duì)端口8688進(jìn)行監(jiān)聽儡羔;然后進(jìn)行while循環(huán),在循環(huán)中阻塞線程直到接收到客戶端消息声旺;收到客戶端消息的時(shí)候會(huì)收到客戶端的Socket笔链,然后開啟while循環(huán)段只,通過InputStream獲取其消息腮猖,通過OutputStream向其發(fā)送消息。
- MainActivity
@Override
protected void onResume() {
super.onResume();
startService(new Intent(this,SocketServerService.class));
}
- SocketServerService
public class SocketServerService extends Service {
private boolean isServiceDestroyed = false;
private static final String TAG = "SocketServerService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new TcpServer()).start();
}
private class TcpServer implements Runnable{
@Override
public void run() {
ServerSocket serverSocket = null;
try{
serverSocket = new ServerSocket(8688);
}catch (IOException exception){
exception.printStackTrace();
}
while(!isServiceDestroyed){
try {
if (serverSocket == null){
return;
}
Log.i(TAG, "ServerSocket loop listen ClientSocket");
//接收客戶端的請(qǐng)求赞枕,并且阻塞直到接收到消息
final Socket client = serverSocket.accept();
new Thread(() -> {
try {
responseClient(client);
} catch (IOException exception) {
exception.printStackTrace();
}
}).start();
}catch (IOException exception){
exception.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.println("你好澈缺,我是服務(wù)端");
while(!isServiceDestroyed){
String str = in.readLine();
Log.i(TAG, "收到客戶端發(fā)來的消息" + str);
if (TextUtils.isEmpty(str)){
//客戶端斷開了連接
Log.i(TAG, "客戶端斷開了連接");
break;
}
String message = "收到了客戶端的消息為:" + str;
// 從客戶端收到的消息加工再發(fā)送給客戶端
out.println(message);
}
out.close();
in.close();
client.close();
}
@Override
public void onDestroy() {
super.onDestroy();
isServiceDestroyed = true;
}
}
3.TestClientSocket
客戶端首先就是開啟while循環(huán),創(chuàng)建Socket與ServerSocket進(jìn)行連接炕婶,直到建立與ServerSocket的連接姐赡;然后同樣是獲得ServerSocket,通過InputStream讀取其內(nèi)容柠掂,通過OutputStream向其寫入內(nèi)容项滑。
public class MainActivity extends AppCompatActivity {
private PrintWriter mPrintWriter;
private EditText mEt_receive;
private TextView mTv_message;
private static final String TAG = "TestClientService";
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
mTv_message.setText(String.format("%s\n客戶端:%s", mTv_message.getText(), message.obj));
mEt_receive.setText("");
return true;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
new Thread(this::connectSocketServer).start();
}
private void initView() {
mEt_receive = findViewById(R.id.et_receive);
Button mBt_send = findViewById(R.id.bt_send);
mTv_message = findViewById(R.id.tv_message);
mBt_send.setOnClickListener(view -> {
final String msg = mEt_receive.getText().toString();
//向服務(wù)器發(fā)送消息
if (!TextUtils.isEmpty(msg) && null!=mPrintWriter){
new Thread(() -> {
mPrintWriter.println(msg);
Message message = Message.obtain();
message.what = 0;
message.obj = msg;
mHandler.sendMessage(message);
}).start();
}
});
}
private void connectSocketServer() {
Socket socket = null;
while (socket == null){
try {
//選擇和服務(wù)器相同的ip和端口 8688
socket = new Socket("192.168.1.3",8688);
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())),true);
}catch (IOException exception){
Log.d(TAG, "connectSocketServer: " + exception);
SystemClock.sleep(1000);
}
}
try {
// 用于接收服務(wù)端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(!isFinishing()){
final String msg = br.readLine();
if (msg != null){
runOnUiThread(() ->
mTv_message.setText(
String.format("%s\n服務(wù)端:%s", mTv_message.getText(), msg)));
}
}
mPrintWriter.close();
br.close();
socket.close();
}catch (IOException exception){
exception.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
- 先打開服務(wù)端
2022-04-30 ... com.example.testserversocket I/SocketServerService: ServerSocket loop listen ClientSocket
4.再打開客戶端
客戶端開啟之后建立與服務(wù)端的連接。
連接創(chuàng)建好之后涯贞,客戶端向服務(wù)端發(fā)送信息枪狂。
2022-04-30 ... com.example.testserversocket I/SocketServerService: 收到客戶端發(fā)來的消息你好,我是客戶端
總結(jié)
在上層宋渔,socket基于對(duì)相同IP和相同端口的監(jiān)聽實(shí)現(xiàn)的州疾。
參考
劉望舒的Android進(jìn)階指北。