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對象的狀態。