Android Socket連接
注意點:注冊訪問的網絡權限;android中UI線程不能有訪問網絡的操作,否則會報android.os.NetworkOnMainThreadException的異常
<uses-permission android:name="android.permission.INTERNET"/>
實例一
客戶端
Java:
package com.android.xiong.simplesocket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; 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 { Socket socket = null; String buffer = ""; TextView txt1; Button send; EditText ed1; String geted1; public Handler myHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0x11) { Bundle bundle = msg.getData(); txt1.append("server:"+bundle.getString("msg")+"\n"); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txt1 = (TextView) findViewById(R.id.txt1); send = (Button) findViewById(R.id.send); ed1 = (EditText) findViewById(R.id.ed1); send.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { geted1 = ed1.getText().toString(); txt1.append("client:"+geted1+"\n"); //啟動線程 向服務器發送和接收信息 new MyThread(geted1).start(); } }); } class MyThread extends Thread { public String txt1; public MyThread(String str) { txt1 = str; } @Override public void run() { //定義消息 Message msg = new Message(); msg.what = 0x11; Bundle bundle = new Bundle(); bundle.clear(); try { //連接服務器 并設置連接超時為5秒 socket = new Socket(); socket.connect(new InetSocketAddress("1.1.9.30", 30000), 5000); //獲取輸入輸出流 OutputStream ou = socket.getOutputStream(); BufferedReader bff = new BufferedReader(new InputStreamReader( socket.getInputStream())); //讀取發來服務器信息 String line = null; buffer=""; while ((line = bff.readLine()) != null) { buffer = line + buffer; } //向服務器發送信息 ou.write("android 客戶端".getBytes("gbk")); ou.flush(); bundle.putString("msg", buffer.toString()); msg.setData(bundle); //發送消息 修改UI線程中的組件 myHandler.sendMessage(msg); //關閉各種輸入輸出流 bff.close(); ou.close(); socket.close(); } catch (SocketTimeoutException aa) { //連接超時 在UI界面顯示消息 bundle.putString("msg", "服務器連接失敗!請檢查網絡是否打開"); msg.setData(bundle); //發送消息 修改UI線程中的組件 myHandler.sendMessage(msg); } catch (IOException e) { e.printStackTrace(); } } } @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; } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <EditText android:id="@+id/ed1" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="給服務器發送信息"/> <Button android:id="@+id/send" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/ed1" android:text="發送"/> <TextView android:id="@+id/txt1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/send"/> </RelativeLayout>
服務端
package com.android.net; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class AndroidService { public static void main(String[] args) throws IOException { ServerSocket serivce = new ServerSocket(30000); while (true) { //等待客戶端連接 Socket socket = serivce.accept(); new Thread(new AndroidRunable(socket)).start(); } } }
package com.android.net; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; public class AndroidRunable implements Runnable { Socket socket = null; public AndroidRunable(Socket socket) { this.socket = socket; } @Override public void run() { // 向android客戶端輸出hello worild String line = null; InputStream input; OutputStream output; String str = "hello world!"; try { //向客戶端發送信息 output = socket.getOutputStream(); input = socket.getInputStream(); BufferedReader bff = new BufferedReader( new InputStreamReader(input)); output.write(str.getBytes("gbk")); output.flush(); //半關閉socket socket.shutdownOutput(); //獲取客戶端的信息 while ((line = bff.readLine()) != null) { System.out.print(line); } //關閉輸入輸出流 output.close(); bff.close(); input.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
實例二
客戶端
Java
package com.android.xiong.sockettwotest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; 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 = new Handler() { @Override public void handleMessage(Message msg) { // 如果消息來自子線程 if (msg.what == 0x123) { // 將讀取的內容追加顯示在文本框中 show.append("\n" + msg.obj.toString()); } } }; clientThread = new ClientThread(handler); // 客戶端啟動ClientThread線程創建網絡連接、讀取來自服務器的數據 new Thread(clientThread).start(); send.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { // 當用戶按下按鈕之后,將用戶輸入的數據封裝成Message // 然后發送給子線程Handler Message msg = new Message(); msg.what = 0x345; msg.obj = input.getText().toString(); clientThread.revHandler.sendMessage(msg); input.setText(""); } catch (Exception e) { } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } }
package com.android.xiong.sockettwotest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import android.os.Handler; import android.os.Looper; import android.os.Message; public class ClientThread implements Runnable { private Socket s; // 定義向UI線程發送消息的Handler對象 Handler handler; // 定義接收UI線程的Handler對象 Handler revHandler; // 該線程處理Socket所對用的輸入輸出流 BufferedReader br = null; OutputStream os = null; public ClientThread(Handler handler) { this.handler = handler; } @Override public void run() { s = new Socket(); try { s.connect(new InetSocketAddress("1.1.9.30", 3000), 5000); br = new BufferedReader(new InputStreamReader(s.getInputStream())); os = s.getOutputStream(); // 啟動一條子線程來讀取服務器相應的數據 new Thread() { @Override public void run() { String content = null; // 不斷的讀取Socket輸入流的內容 try { while ((content = br.readLine()) != null) { // 每當讀取到來自服務器的數據之后,發送的消息通知程序 // 界面顯示該數據 Message msg = new Message(); msg.what = 0x123; msg.obj = content; handler.sendMessage(msg); } } catch (IOException io) { io.printStackTrace(); } } }.start(); // 為當前線程初始化Looper Looper.prepare(); // 創建revHandler對象 revHandler = new Handler() { @Override public void handleMessage(Message msg) { // 接收到UI線程的中用戶輸入的數據 if (msg.what == 0x345) { // 將用戶在文本框輸入的內容寫入網絡 try { os.write((msg.obj.toString() + "\r\n") .getBytes("gbk")); } catch (Exception e) { e.printStackTrace(); } } } }; // 啟動Looper Looper.loop(); } catch (SocketTimeoutException e) { Message msg = new Message(); msg.what = 0x123; msg.obj = "網絡連接超時!"; handler.sendMessage(msg); } catch (IOException io) { io.printStackTrace(); } } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <EditText android:id="@+id/input" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/input" /> <Button android:id="@+id/send" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/send" android:layout_below="@id/input"/> <TextView android:id="@+id/show" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/send"/> </RelativeLayout>
服務端
package com.android.net; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class MyService { // 定義保存所有的Socket public static List<Socket> socketList = new ArrayList<Socket>(); public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(3000); while(true){ Socket s=server.accept(); socketList.add(s); //每當客戶端連接之后啟動一條ServerThread線程為該客戶端服務 new Thread(new ServiceThreada(s)).start(); } } }
package com.android.net; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; public class ServiceThreada implements Runnable { // 定義當前線程處理的Socket Socket s = null; // 該線程所處理的Socket所對應的輸入流 BufferedReader br = null; public ServiceThreada(Socket s) { this.s = s; try { br = new BufferedReader(new InputStreamReader(s.getInputStream())); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { String content = null; //采用循環不斷的從Socket中讀取客戶端發送過來的數據 while((content=readFromClient())!=null){ //遍歷socketList中的每個Socket //將讀取到的內容每個向Socket發送一次 for(Socket s:MyService.socketList){ OutputStream os; try { os = s.getOutputStream(); os.write((content+"\n").getBytes("gbk")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } // 定義讀取客戶端的信息 public String readFromClient() { try { return br.readLine(); } catch (Exception e) { e.printStackTrace(); } return null; } }
附:
一些記錄:
把socket寫成一個服務:
public class SocketService extends Service{ Thread mServiceThread; Socket client; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); mServiceThread = new Thread(new SocketServerThread()); } @Override public void onStart(Intent intent, int startId) { // TODO Auto-generated method stub super.onStart(intent, startId); mServiceThread.start(); } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); } public class SocketServerThread extends Thread { private static final int PORT = 54321; private SocketServerThread(){ } @Override public void run() { try { ServerSocket server = new ServerSocket(PORT); while(true){ System.out.println("begin client connected"); client = server.accept(); System.out.println("client connected"); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); System.out.println("read from client:"); String textLine = reader.readLine(); if(textLine.equalsIgnoreCase("EXIT")){ System.out.println("EXIT invoked, closing client"); break; } System.out.println(textLine); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))); writer.println("ECHO from server: " + textLine); writer.flush(); writer.close(); reader.close(); } } catch (IOException e) { // TODO Auto-generated catch block System.err.println(e); } } } }
狀態:
除了isClose方法,Socket類還有一個isConnected方法來判斷Socket對象是否連接成功。 看到這個名字,也許讀者會產生誤解。 其實isConnected方法所判斷的并不是Socket對象的當前連接狀態, 而是Socket對象是否曾經連接成功過,如果成功連接過,即使現在isClose返回true, isConnected仍然返回true。因此,要判斷當前的Socket對象是否處于連接狀態, 必須同時使用isClose和isConnected方法, 即只有當isClose返回false,isConnected返回true的時候Socket對象才處于連接狀態。 雖然在大多數的時候可以直接使用Socket類或輸入輸出流的close方法關閉網絡連接,但有時我們只希望關閉OutputStream或InputStream,而在關閉輸入輸出流的同時,并不關閉網絡連接。這就需要用到Socket類的另外兩個方法:shutdownInput和shutdownOutput,這兩個方法只關閉相應的輸入、輸出流,而它們并沒有同時關閉網絡連接的功能。和isClosed、isConnected方法一樣,Socket類也提供了兩個方法來判斷Socket對象的輸入、輸出流是否被關閉,這兩個方法是isInputShutdown()和isOutputShutdown()。 shutdownInput和shutdownOutput并不影響Socket對象的狀態。