(1) 起始狀態
(2) 啟動Server
(3) Client端連線+發送訊息
備註:當Server端收到Client端的訊息後,會發送回應給Client端
Sample code following:
package com.example.sample_niotcpserver; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.Set; import org.apache.http.conn.util.InetAddressUtils; import com.example.sample_niotcpserver.R.id; import android.app.Activity; import android.os.Bundle; import android.util.Log; 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 implements OnClickListener { //===== Debug Member Variables =====// private static String Class_TAG = "MainActivity"; private static String Debug_TAG = "Debug Use"; //===== UI Member Variables =====// private Button mBtnBind, mBtnClose; private TextView mTvHint, mTvPostMsg; private EditText mEtPort; //===== nioTCP Member Variables =====// private ServerSocketChannel mServerSocketChannel = null; private Selector mSelector = null; private String mLocalIP = "0.0.0.0"; private int mLocalPort = 0; private boolean isRunning = false; // Server open state. // Record the remote socket, IP and port. private ArrayList<Socket> mSocketList; private ArrayList<String> mRemoteIPList; private ArrayList<Integer>mRemotePortList; // When the data can't write to client, need to record the answer data to wait for until it can write. private String mAnswerTemp = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtnBind = (Button) this.findViewById(R.id.xml_btn_bind); mBtnBind.setOnClickListener(this); mBtnBind.setEnabled(true); mBtnClose = (Button) this.findViewById(R.id.xml_btn_close); mBtnClose.setOnClickListener(this); mBtnClose.setEnabled(false); mTvHint = (TextView) this.findViewById(R.id.xml_tv_hint); mTvPostMsg = (TextView) this.findViewById(R.id.xml_tv_post_msg); mEtPort = (EditText) this.findViewById(R.id.xml_et_port); mEtPort.setText("8000"); } @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; } // The button listener method. @Override public void onClick(View v) { switch (v.getId()){ case id.xml_btn_bind: // Close the bind button. mBtnBind.setEnabled(false); mTvPostMsg.setText(""); mSocketList = new ArrayList<Socket>(); mSocketList.clear(); mRemoteIPList = new ArrayList<String>(); mRemoteIPList.clear(); mRemotePortList = new ArrayList<Integer>(); mRemotePortList.clear(); mLocalIP = getLocalIP(); mLocalPort = Integer.valueOf(mEtPort.getText().toString()); mTvHint.setText("Starting server..."); new Thread(nioTCPSetverThread).start(); break; case id.xml_btn_close: // Close the close button. mBtnClose.setEnabled(false); mTvHint.setText("Closing server..."); // Change server state. isRunning = false; mSelector.wakeup(); break; } } private Runnable nioTCPSetverThread = new Runnable(){ @Override public void run() { try { // Open the seletor. mSelector = Selector.open(); try { // Open the local TCP server socket channel. mServerSocketChannel = ServerSocketChannel.open(); // Configure blocking mode: true(blocking) false(non-blocking). mServerSocketChannel.configureBlocking(false); // Bind the server with lacal IP address and port. mServerSocketChannel.socket().bind(new InetSocketAddress(mLocalIP, mLocalPort)); // Server socket channel registers OP_ACCEPT command to server selector, and generate a SelectionKey. mServerSocketChannel.register(mSelector, SelectionKey.OP_ACCEPT); // Change the button state. runOnUiThread(new Runnable(){ @Override public void run() { mBtnClose.setEnabled(true); mTvHint.setText("Server bind. IP: "+ mLocalIP + " Port: "+ mLocalPort); }}); // Change server state. isRunning = true; try { while(isRunning == true) { // Selector try to find the channel which has registered events. mSelector.select(); Set<SelectionKey> ServerChannelKeys = mSelector.selectedKeys(); Iterator<SelectionKey> keyIter = ServerChannelKeys.iterator(); while (keyIter.hasNext() && (mSelector.isOpen() == true)) {// If keyIter has any event, return true, or retun false. // Extract the event into key from keyIter. SelectionKey key = (SelectionKey) keyIter.next(); // Remove keyIter when the key takes the event. keyIter.remove(); if (key.isAcceptable()) {// Accept connect. // Accept the client socket from the server channel. final SocketChannel ClientSocketChannel = ((ServerSocketChannel) key.channel()).accept(); // Configure blocking mode: true(blocking) false(non-blocking). ClientSocketChannel.configureBlocking(false); // Client socket channel registers "OP_READ" command to server selector. ClientSocketChannel.register(mSelector, SelectionKey.OP_READ); // Add the connectable socket to socket list. if (mSocketList.contains(ClientSocketChannel.socket()) == false) { mSocketList.add(ClientSocketChannel.socket()); int socketIndex = mSocketList.indexOf(ClientSocketChannel.socket()); mRemoteIPList.add(socketIndex, ClientSocketChannel.socket().getInetAddress().toString()); mRemotePortList.add(socketIndex, ClientSocketChannel.socket().getPort()); } // Post accept message to UI msg TextView. runOnUiThread(new Runnable(){ @Override public void run() { mTvPostMsg.append("IP:"+ ClientSocketChannel.socket().getInetAddress().toString() + "(" + ClientSocketChannel.socket().getPort() + ")" + "is online.\r\n"); }}); }// if (key.isAcceptable()) else if (key.isReadable()) {// Receive data. ByteBuffer readBuffer = ByteBuffer.allocate(10 * 2048); final SocketChannel ClientSocketChannel = (SocketChannel) key.channel(); int readBuffLen = ClientSocketChannel.read(readBuffer); if (readBuffLen < 0) {// Client side socket has disconnected. // Remove the disconnectable socket from socket list. if (mSocketList.contains(ClientSocketChannel.socket()) == true) { final int socketIndex = mSocketList.indexOf(ClientSocketChannel.socket()); // Post close message to UI msg TextView. runOnUiThread(new Runnable(){ @Override public void run() { mTvPostMsg.append("IP:"+ mRemoteIPList.get(socketIndex) + "(" + mRemotePortList.get(socketIndex) + ")" + "is offline.\r\n"); mRemoteIPList.remove(socketIndex); mRemotePortList.remove(socketIndex); }}); mSocketList.remove(ClientSocketChannel.socket()); } // Close the channel. key.cancel(); key.channel().close(); } else { // Update the index of "position" and "limit", change position to "0"; limit to "valid data length". readBuffer.flip(); int ValidDataLen = readBuffer.limit(); final byte[] ReceiveData = new byte[ValidDataLen]; readBuffer.get(ReceiveData); // Post read message to UI msg TextView. runOnUiThread(new Runnable(){ @Override public void run() { String msg = new String(ReceiveData); mTvPostMsg.append("IP:"+ ClientSocketChannel.socket().getInetAddress().toString() + "(" + ClientSocketChannel.socket().getPort() + ")" + "say: " + msg + "\r\n"); }}); // Try to answer the data to client. String answer = "Server say: your message: \""+ (new String(ReceiveData)) + "\"has received.\r\n"; ByteBuffer writeBuffer = ByteBuffer.wrap(answer.getBytes()); int writeLength = ((SocketChannel) key.channel()).write(writeBuffer);; if (writeLength == 0) {// The channel buffer is full. // Record the answer data. mAnswerTemp = answer; // Register the OP_WRITE command. key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); } } } // else if (key.isReadable()) else if (key.isWritable()) { ByteBuffer writeBuffer = ByteBuffer.wrap(mAnswerTemp.getBytes()); ((SocketChannel) key.channel()).write(writeBuffer); mAnswerTemp = null; // Cancel the OP_WRITE command. key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); } // else if (key.isWritable()) }// while (keyIter.hasNext() && (mSelector.isOpen() == true)) }// while(isRunning == true) } catch (IOException e) { e.printStackTrace(); Log.v(Class_TAG, "nioTCPSetverThread"); Log.e(Debug_TAG, "The selecter selects fail."); } } catch (IOException e) { e.printStackTrace(); Log.v(Class_TAG, "nioTCPSetverThread"); Log.e(Debug_TAG, "Server socket channel \"open\" or \"bind\" fail."); } } catch (IOException e) { e.printStackTrace(); Log.v(Class_TAG, "nioTCPSetverThread"); Log.e(Debug_TAG, "Selector open fail."); } try { // Close server channel and selector. mServerSocketChannel.keyFor(mSelector).cancel(); mServerSocketChannel.socket().close(); mServerSocketChannel.close(); mServerSocketChannel = null; mSelector.close(); mSelector = null; // Change the button state. runOnUiThread(new Runnable(){ @Override public void run() { mBtnBind.setEnabled(true); mTvHint.setText("Server close."); }}); } catch (IOException e) { e.printStackTrace(); Log.v(Class_TAG, "nioTCPSetverThread"); Log.e(Debug_TAG, "Close server socket channel or selector fail."); } } }; private String getLocalIP() { try { String ipv4; ArrayList<NetworkInterface> nilist = Collections.list(NetworkInterface.getNetworkInterfaces()); for (NetworkInterface ni: nilist) { ArrayList<InetAddress> ialist = Collections.list(ni.getInetAddresses()); for (InetAddress address: ialist){ if (!address.isLoopbackAddress() && InetAddressUtils.isIPv4Address(ipv4=address.getHostAddress())) { return ipv4; } } } } catch (SocketException ex) { Log.v(Class_TAG, "getLocalIP"); Log.e(Debug_TAG, "Error: get local IP address."); } return null; } }activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:id="@+id/xml_tv_hint" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:hint="Hint" android:textColor="#000000" android:textSize="30sp" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Port number:" android:textColor="#000000" android:textSize="20sp" /> <EditText android:id="@+id/xml_et_port" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:ems="10" android:hint="Enter the number 0~65535" android:textSize="20sp" > <requestFocus /> </EditText> <Button android:id="@+id/xml_btn_bind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0.54" android:text="Bind" /> <Button android:id="@+id/xml_btn_close" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0.42" android:text="Close" /> </LinearLayout> <TextView android:id="@+id/xml_tv_post_msg" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="20dp" /> </LinearLayout>AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sample_niotcpserver" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.sample_niotcpserver.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
沒有留言 :
張貼留言