Android網路程式設計的Socket通訊總結
阿新 • • 發佈:2019-02-15
建立伺服器端的步驟:
1,指定埠例項化一個ServerSocket
2,呼叫ServerSocket的accept方法等待連線期間阻塞
3,獲取位於底層的Socket流進行讀寫操作
4,將資料封裝成流
5,對Socket進行讀寫
6,關閉流
建立客戶端的步驟:
1,通過IP地址和埠例項化Socket,請求連線伺服器
2,獲取位於底層的Socket流進行讀寫操作
3,將資料封裝成流(BufferedReader/PrintWriter,DataOutputStream/DataInputStream)的例項
4,對Socket進行讀寫
5,關閉流
使用ServerSocket建立伺服器端:
客戶端使用Socket通訊:public static void main(String[] args) throws IOException { // TODO Auto-generated method stub //建立一個ServerSo檢視特,用於監聽客戶端Socket的連線請求 ServerSocket ss=new ServerSocket(3000); while(true){ //每當接收到客戶端Socket的請求,伺服器端也對應產生一個Socket,沒接收到請求就等待。。 Socket s=ss.accept(); OutputStream os=s.getOutputStream();//伺服器端產生的Socket獲取輸出流 os.write("您好,您收到了來自伺服器的祝福!\n".getBytes()); os.close(); s.close(); } }
測試環境是PC端伺服器,手機當客戶端,PC和手機要連線同一個區域網,PC和手機在同一網段
package com.example.simpleclient; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends Activity { TextView text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text=(TextView) findViewById(R.id.text); new Thread(){ @Override public void run() { // TODO Auto-generated method stub try { //建立連線到遠端伺服器的的Socket,Ip是伺服器端PC的IP,測試環境是PC端伺服器,手機當客戶端,PC和手機要連線同一個區域網,PC和手機在同一網段 Socket socket = new Socket("192.168.88",3000); //將Socket對應的輸入流包裝秤BufferedReader BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream())); String line=br.readLine(); text.setText("來自服務的資料:"+line); br.close(); socket.close(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }.start(); } }
<!-- 訪問網路許可權 -->
<uses-permission android:name="android.permission.INTERNET"/>
客戶端和伺服器端的輸入輸出流的問題容易搞混淆:如下圖
在客戶端:
socket.getInputStream();從socket讀來自伺服器的資料
socket.getOutputStream();向socket中寫資料,傳給伺服器,伺服器在它的socket的輸入流讀這個資料
在伺服器端:
socket.getInputStream();從socket讀來自客戶端的資料
socket.getOutputStream();向socket中寫資料,傳給客戶端,客戶端在它的socket的輸入流讀這個資料
輸入輸出流的包裝:
第一種方法:資料二進位制流
DataInputStream in=new DataInputStream(socket.getInputStream());//接收客戶端資訊
DataOutputStream out=new DataOutputStream(socket.getOutputStream()); //向客戶端傳送訊息
第二種方法:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
socketClient,java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class socketClient extends Activity {
private Button button;
private TextView text;
private EditText edit;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = (Button) findViewById(R.id.button);
edit = (EditText) findViewById(R.id.edit);
text = (TextView) findViewById(R.id.text);
button.setOnClickListener(new View.OnClickListener() {
private Socket socket = null;
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
String sendMsg = edit.getText().toString() + "\r\n";
try {
socket = new Socket("192.168.0.37", 8888); // 建立Socket,其中ip地址為我的PC機器的地址,手機通過wifi上網和伺服器在一個網段
// PrintWriter out = new PrintWriter(new BufferedWriter(new
// OutputStreamWriter(socket.getOutputStream())),true);
// out.println(sendMsg);
//
// BufferedReader in = new BufferedReader(new
// InputStreamReader(socket.getInputStream()));
// String readMsg = in.readLine();
// if(readMsg !=null){
// text.setText(readMsg);
// }else{
// text.setText("錯誤");
// }
//
// out.close();
// in.close();
// socket.close();
DataOutputStream out = new DataOutputStream(socket
.getOutputStream()); // 向伺服器傳送訊息
out.writeUTF(sendMsg);
out.flush();
DataInputStream in = new DataInputStream(socket
.getInputStream()); // 接收來自伺服器的訊息
String readMsg = in.readUTF();
if (readMsg != null) {
text.setText(readMsg);
}
out.close();
in.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Server.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public Server(){
new ServerThread().start();
}
class ServerThread extends Thread{
public void run() {
try {
ServerSocket ss=new ServerSocket(8888); ////建立一個ServerSocket物件,並讓這個ServerSocket在8888埠監聽
while(true){
Socket socket=ss.accept(); //呼叫ServerSocket的accept()方法,接受客戶端所傳送的請求,如果客戶端沒有傳送資料,那麼該執行緒就停滯不繼續
// try {
// BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //接收客戶端資訊
// String readline = in.readLine();
// System.out.println("readline:"+readline);
//
// PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
// out.println("link server success");
//
// in.close(); //關閉流
// out.close();//關閉流
// socket.close();//關閉開啟的socket
//
// } catch (Exception e) {
// // TODO: handle exception
// }finally{
// // socket.close();//
// }
try {
DataInputStream in=new DataInputStream(socket.getInputStream());//接收客戶端資訊
String readline=in.readUTF();
System.out.println(readline);
DataOutputStream out=new DataOutputStream(socket.getOutputStream()); //向客戶端傳送訊息
out.writeUTF("link server success");
out.flush();
in.close(); //關閉流
out.close();//關閉流
socket.close();//關閉開啟的socket
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
public static void main(String[] args) throws IOException {
new Server(); //開啟伺服器
}
}
加入多執行緒:聊天室
客戶端和伺服器端保持長時間的通訊,伺服器需要不斷的讀取客戶端資料,並向客戶端寫入資料,客戶端也需要不斷的讀取伺服器的資料
伺服器應該為每個Socket單獨啟動一條執行緒,每條執行緒負責與一個客戶端進行通訊
伺服器端:
package com.hust.multithred;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class MyServer {
/**
* @param args
* @throws IOException
*/
//伺服器端儲存所有Socket的ArrayList
public static ArrayList<Socket> socketlist=new ArrayList<Socket>();
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
ServerSocket ss=new ServerSocket(3000);//ServerSocket監聽3000埠
while(true){
Socket socket_in_server=ss.accept();//迴圈等待客戶端的Socket
socketlist.add(socket_in_server); //每接收到一個客戶端的Socket,將伺服器端產生的與之對應的Socket加入陣列
//為每一個Socket單獨啟動一條執行緒,每個執行緒負責與一個客戶端進行通訊
SocketThread socketthread=new SocketThread(socket_in_server);
new Thread(socketthread).start();
}
}
}
package com.hust.multithred;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class SocketThread implements Runnable{//執行緒任務類,實現Runnable介面
Socket s=null;
BufferedReader br=null;
public SocketThread(Socket s) throws IOException{
this.s=s;
br=new BufferedReader(new InputStreamReader(s.getInputStream()));//Socket輸入流包裝成字元流,來自客戶端的資料在此輸入流上,伺服器端可以讀
}
public void run() {
// TODO Auto-generated method stub
try {
String content=null;
//迴圈不斷衝Socket中讀取客戶端傳送過來的資料
while((content=readFormClient())!=null){
//每讀到資料之後,將讀到的內容向每個Socket傳送一次
for(Socket s:MyServer.socketlist){
OutputStream os=s.getOutputStream();
os.write((content+"\n").getBytes("utf-8")); //寫到每個socket 的輸出流上
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//從輸入流上讀取來自客戶端的資料方法
public String readFormClient(){
String content=null;
try {
content = br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
MyServer.socketlist.remove(s);
}
return content;
}
}
客戶端:
MainActivity.java
package com.hust.multithreadclient;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
EditText input;
TextView show;
Button send;
Handler handler;
ClientThread clientthread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
input=(EditText) findViewById(R.id.input);
show=(TextView) findViewById(R.id.show);
send=(Button) findViewById(R.id.send);
//此處handler接收來自子執行緒的訊息,負責處理訊息,更新UI
handler=new Handler(){
@Override
//如果訊息來自子執行緒
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if(msg.what==0x123){
show.append("\n"+msg.obj.toString());
}
}
};
//客戶端啟動ClientThread執行緒建立玩過連線,讀取來自伺服器的資料
new Thread(new ClientThread(handler)).start();
send.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// 當用戶按下發送按鈕後,將使用者輸入的資料封裝成Message,發給子執行緒的Handler,此處handler負責傳送訊息
Message msg=new Message();
msg.what=0x111;
msg.obj=input.getText().toString();
clientthread.rvhandler.sendMessage(msg);//發給子執行緒的Handler
input.setText("");
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
ClientThread.java
package com.hust.multithreadclient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
public class ClientThread implements Runnable {
Socket s;
Handler handler; //定義向UI執行緒傳送訊息的Handler物件
Handler rvhandler; //定義接收UI執行緒訊息的Handler物件
BufferedReader br=null;
OutputStream os=null;
public ClientThread(Handler handler){
this.handler=handler;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
s=new Socket("192.168.1.88",3000);
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
os=s.getOutputStream();
//啟動一條子執行緒來讀取伺服器端相應的資料
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
String content=null;
try{
while((content=br.readLine())!=null){
Message msg=new Message();
msg.what=0x123;
msg.obj=content;
handler.sendMessage(msg);//此子執行緒中僅僅是讀來自伺服器的資料,併發給UI執行緒的Handler處理
}
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
/*
* 當前客戶端執行緒中Handler,Android執行緒之間的通訊是Handler來實現
* 此Hnadler接收UI執行緒發過來的資料,即使用者的輸入,並寫到輸出流
* 因為此Handler是接收處理訊息,所以需要使用Looper
* */
Looper.prepare();
rvhandler=new Handler(){
@Override
public void handleMessage(Message msg) {
//如果接收到UI執行緒中使用者輸入的資料
if(msg.what==0x111){
try{
//將使用者在文字框內的輸入內容寫入網路
os.write((msg.obj.toString()+"\r\n").getBytes("utf-8"));
}catch(IOException e){
e.printStackTrace();
}
}
}
};
Looper.loop();//啟動looper
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}