一、概述
關于Socket程式設計的基本方法在基礎篇裡已經講過,今天把它給完善了。加入了多線程,這樣UI線程就不會被阻塞;實作了用戶端和伺服器的雙向通信,隻要用戶端發起了連接配接并成功連接配接後那麼兩者就可以随意進行通信了。
二、實作
在之前的工程基礎上進行修改就可以了。
MyClient工程的main.xml檔案不用修改,隻需要修改MyClientActivity.java檔案,主要是定義了一個繼承自Thread類的用于接收資料的類,覆寫了其中的run()方法,在這個函數裡面接收資料,接收到資料後就通過Handler發送消息,收到消息後在UI線程裡更新接收到的資料。完整的内容如下:
package com.nan.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MyClientActivity extends Activity
{
private EditText mEditText = null;
private Button connectButton = null;
private Button sendButton = null;
private TextView mTextView = null;
private Socket clientSocket = null;
private OutputStream outStream = null;
private Handler mHandler = null;
private ReceiveThread mReceiveThread = null;
private boolean stop = true;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEditText = (EditText)this.findViewById(R.id.edittext);
mTextView = (TextView)this.findViewById(R.id.retextview);
connectButton = (Button)this.findViewById(R.id.connectbutton);
sendButton = (Button)this.findViewById(R.id.sendbutton);
sendButton.setEnabled(false);
//連接配接按鈕監聽
connectButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
try
{
//執行個體化對象并連接配接到伺服器
clientSocket = new Socket("113.114.170.246",8888);
}
catch (UnknownHostException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
displayToast("連接配接成功!");
//連接配接按鈕使能
connectButton.setEnabled(false);
//發送按鈕使能
sendButton.setEnabled(true);
mReceiveThread = new ReceiveThread(clientSocket);
stop = false;
//開啟線程
mReceiveThread.start();
}
});
//發送資料按鈕監聽
sendButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
byte[] msgBuffer = null;
//獲得EditTex的内容
String text = mEditText.getText().toString();
try {
//字元編碼轉換
msgBuffer = text.getBytes("GB2312");
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
//獲得Socket的輸出流
outStream = clientSocket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
//發送資料
outStream.write(msgBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//清空内容
mEditText.setText("");
displayToast("發送成功!");
}
});
//消息處理
mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
//顯示接收到的内容
mTextView.setText((msg.obj).toString());
}
};
}
//顯示Toast函數
private void displayToast(String s)
{
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
private class ReceiveThread extends Thread
{
private InputStream inStream = null;
private byte[] buf;
private String str = null;
ReceiveThread(Socket s)
{
try {
//獲得輸入流
this.inStream = s.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run()
{
while(!stop)
{
this.buf = new byte[512];
try {
//讀取輸入資料(阻塞)
this.inStream.read(this.buf);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//字元編碼轉換
try {
this.str = new String(this.buf, "GB2312").trim();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg = new Message();
msg.obj = this.str;
//發送消息
mHandler.sendMessage(msg);
}
}
}
@Override
public void onDestroy()
{
super.onDestroy();
if(mReceiveThread != null)
{
stop = true;
mReceiveThread.interrupt();
}
}
}
對于MyServer工程,改動比較大,首先是main.xml檔案,在裡面添加了兩個TextView,一個用于顯示用戶端的IP,一個用于顯示接收到的内容,一個用于發送資料的Button,還有一個EditText,完整的main.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/iptextview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dip"
android:gravity="center_horizontal"
/>
<EditText
android:id="@+id/sedittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="請輸入要發送的内容"
/>
<Button
android:id="@+id/sendbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="發送"
/>
<TextView
android:id="@+id/textview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="15dip"
/>
</LinearLayout>
接着,修改MyServerActivity.java檔案,定義了兩個Thread子類,一個用于監聽用戶端的連接配接,一個用于接收資料,其他地方與MyClientActivity.java差不多。完整的内容如下:
package com.nan.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MyServerActivity extends Activity
{
private TextView ipTextView = null;
private EditText mEditText = null;
private Button sendButton = null;
private TextView mTextView = null;
private OutputStream outStream = null;
private Socket clientSocket = null;
private ServerSocket mServerSocket = null;
private Handler mHandler = null;
private AcceptThread mAcceptThread = null;
private ReceiveThread mReceiveThread = null;
private boolean stop = true;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ipTextView = (TextView)this.findViewById(R.id.iptextview);
mEditText = (EditText)this.findViewById(R.id.sedittext);
sendButton = (Button)this.findViewById(R.id.sendbutton);
sendButton.setEnabled(false);
mTextView = (TextView)this.findViewById(R.id.textview);
//發送資料按鈕監聽
sendButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
byte[] msgBuffer = null;
//獲得EditTex的内容
String text = mEditText.getText().toString();
try {
//字元編碼轉換
msgBuffer = text.getBytes("GB2312");
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
//獲得Socket的輸出流
outStream = clientSocket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
//發送資料
outStream.write(msgBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//清空内容
mEditText.setText("");
displayToast("發送成功!");
}
});
//消息處理
mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
switch(msg.what)
{
case 0:
{
//顯示用戶端IP
ipTextView.setText((msg.obj).toString());
//使能發送按鈕
sendButton.setEnabled(true);
break;
}
case 1:
{
//顯示接收到的資料
mTextView.setText((msg.obj).toString());
break;
}
}
}
};
mAcceptThread = new AcceptThread();
//開啟監聽線程
mAcceptThread.start();
}
//顯示Toast函數
private void displayToast(String s)
{
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
private class AcceptThread extends Thread
{
@Override
public void run()
{
try {
//執行個體化ServerSocket對象并設定端口号為8888
mServerSocket = new ServerSocket(8888);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
//等待用戶端的連接配接(阻塞)
clientSocket = mServerSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mReceiveThread = new ReceiveThread(clientSocket);
stop = false;
//開啟接收線程
mReceiveThread.start();
Message msg = new Message();
msg.what = 0;
//擷取用戶端IP
msg.obj = clientSocket.getInetAddress().getHostAddress();
//發送消息
mHandler.sendMessage(msg);
}
}
private class ReceiveThread extends Thread
{
private InputStream mInputStream = null;
private byte[] buf ;
private String str = null;
ReceiveThread(Socket s)
{
try {
//獲得輸入流
this.mInputStream = s.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run()
{
while(!stop)
{
this.buf = new byte[512];
//讀取輸入的資料(阻塞讀)
try {
this.mInputStream.read(buf);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//字元編碼轉換
try {
this.str = new String(this.buf, "GB2312").trim();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg = new Message();
msg.what = 1;
msg.obj = this.str;
//發送消息
mHandler.sendMessage(msg);
}
}
}
@Override
public void onDestroy()
{
super.onDestroy();
if(mReceiveThread != null)
{
stop = true;
mReceiveThread.interrupt();
}
}
}
兩個工程都修改好了,同樣,在模拟器上運作用戶端: