示例(使用AsyncTask的Android双向网络套接字)

敢说敢作敢为, 无怨无恨无悔。这篇文章主要讲述示例:使用AsyncTask的Android双向网络套接字相关的知识,希望能为你提供帮助。
我在android上找到的大多数网络套接字示例都是单向的。我需要一个双向数据流的解决方案。我最终了解到了AsyncTask。此示例显示如何从套接字获取数据并将数据发送回它。由于正在接收数据的套接字的阻塞性质,阻塞需要在UI线程以外的线程中运行。
为了举例,此代码连接到Web服务器。按“Start AsyncTask”按钮将打开套接字。套接字打开后,Web服务器将等待请求。按“发送消息”按钮将向服务器发送请求。服务器的任何响应都将显示在TextView中。对于http,一旦发送了所有数据,Web服务器将断开与客户端的连接。对于其他TCP数据流,连接将一直保持到一侧断开连接。
截图:

AndroidManifest.xml中:

< ?xml version="1.0" encoding="utf-8"?> < manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.exampleasynctask" android:versionCode="1" android:versionName="1.0"> < uses-sdk android:minSdkVersion="8" /> < uses-permission android:name="android.permission.INTERNET" /> < application android:icon="@drawable/icon" android:label="@string/app_name"> < activity android:name=".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>

【示例(使用AsyncTask的Android双向网络套接字)】水库布局 main.xml中:
< ?xml version="1.0" encoding="utf-8"?> < LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > < Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start AsyncTask"> < /Button> < Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send Message"> < /Button> < TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here" /> < /LinearLayout>

SRCcom.exampleasynctaskMainActivity.java:
package com.exampleasynctask; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { Button btnStart, btnSend; TextView textStatus; NetworkTask networktask; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnStart = (Button)findViewById(R.id.btnStart); btnSend = (Button)findViewById(R.id.btnSend); textStatus = (TextView)findViewById(R.id.textStatus); btnStart.setOnClickListener(btnStartListener); btnSend.setOnClickListener(btnSendListener); networktask = new NetworkTask(); //Create initial instance so SendDataToNetwork doesn't throw an error. }private OnClickListener btnStartListener = new OnClickListener() { public void onClick(View v){ btnStart.setVisibility(View.INVISIBLE); networktask = new NetworkTask(); //New instance of NetworkTask networktask.execute(); } }; private OnClickListener btnSendListener = new OnClickListener() { public void onClick(View v){ textStatus.setText("Sending Message to AsyncTask."); networktask.SendDataToNetwork("GET / HTTP/1.1"); } }; public class NetworkTask extends AsyncTask< Void, byte[], Boolean> { Socket nsocket; //Network Socket InputStream nis; //Network Input Stream OutputStream nos; //Network Output Stream@Override protected void onPreExecute() { Log.i("AsyncTask", "onPreExecute"); }@Override protected Boolean doInBackground(Void... params) { //This runs on a different thread boolean result = false; try { Log.i("AsyncTask", "doInBackground: Creating socket"); SocketAddress sockaddr = new InetSocketAddress("192.168.1.1", 80); nsocket = new Socket(); nsocket.connect(sockaddr, 5000); //10 second connection timeout if (nsocket.isConnected()) { nis = nsocket.getInputStream(); nos = nsocket.getOutputStream(); Log.i("AsyncTask", "doInBackground: Socket created, streams assigned"); Log.i("AsyncTask", "doInBackground: Waiting for inital data..."); byte[] buffer = new byte[4096]; int read = nis.read(buffer, 0, 4096); //This is blocking while(read != -1){ byte[] tempdata = https://www.songbingjia.com/android/new byte[read]; System.arraycopy(buffer, 0, tempdata, 0, read); publishProgress(tempdata); Log.i("AsyncTask", "doInBackground: Got some data"); read = nis.read(buffer, 0, 4096); //This is blocking } } } catch (IOException e) { e.printStackTrace(); Log.i("AsyncTask", "doInBackground: IOException"); result = true; } catch (Exception e) { e.printStackTrace(); Log.i("AsyncTask", "doInBackground: Exception"); result = true; } finally { try { nis.close(); nos.close(); nsocket.close(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } Log.i("AsyncTask", "doInBackground: Finished"); } return result; }public void SendDataToNetwork(String cmd) { //You run this from the main thread. try { if (nsocket.isConnected()) { Log.i("AsyncTask", "SendDataToNetwork: Writing received message to socket"); nos.write(cmd.getBytes()); } else { Log.i("AsyncTask", "SendDataToNetwork: Cannot send message. Socket is closed"); } } catch (Exception e) { Log.i("AsyncTask", "SendDataToNetwork: Message send failed. Caught an exception"); } }@Override protected void onProgressUpdate(byte[]... values) { if (values.length > 0) { Log.i("AsyncTask", "onProgressUpdate: " + values[0].length + " bytes received."); textStatus.setText(new String(values[0])); } } @Override protected void onCancelled() { Log.i("AsyncTask", "Cancelled."); btnStart.setVisibility(View.VISIBLE); } @Override protected void onPostExecute(Boolean result) { if (result) { Log.i("AsyncTask", "onPostExecute: Completed with an Error."); textStatus.setText("There was a connection error."); } else { Log.i("AsyncTask", "onPostExecute: Completed."); } btnStart.setVisibility(View.VISIBLE); } }@Override protected void onDestroy() { super.onDestroy(); networktask.cancel(true); //In case the task is currently running } }

答案SendDataToNetwork任务在主ui线程中运行,这意味着由于NetworkOnMainThreadException致命异常,它将导致Honeycomb或更高版本的应用程序崩溃。这是我的SendDataToNetwork想要避免这个问题:
public boolean sendDataToNetwork(final byte[] cmd) { if (_nsocket.isConnected()) { Log.i(TAG, "SendDataToNetwork: Writing received message to socket"); new Thread(new Runnable() { public void run() { try { _nos.write(cmd); } catch (Exception e) { e.printStackTrace(); Log.i(TAG, "SendDataToNetwork: Message send failed. Caught an exception"); } } }).start(); return true; }Log.i(TAG, "SendDataToNetwork: Cannot send message. Socket is closed"); return false; }

另一答案你的SendDataToNetwork不会和doInBackground()在同一个线程上运行。 SendDataToNetwork有可能在套接字准备好之前开始发送数据。
为了避免这一切,只需使用SendDataToNetwork将数据和信号保存到后台线程,即可以发送数据。
由于用户可能会多次按下按钮,而旧数据仍在发送,因此您应该在NetworkTask中同步Queue。然后:
  1. 后台线程设置套接字连接,然后进入休眠状态(通过wait())。
  2. 按下按钮,SendDataToNetwork将数据添加到队列并唤醒后台线程(通过notify())。
  3. 当后台线程唤醒时,它首先检查finish标志。如果设置,它将关闭连接并退出。如果不是,它从队列中读取数据,将其发送到网络并返回休眠状态。
  4. 你应该有finish()方法,它设置一个finish标志(原子变量,像布尔值)并唤醒后台线程。这是一种优雅地退出后台线程的方法。
看一下线程同步的完成方式:http://www.jchq.net/tutorial/07_03Tut.htm
另一答案更多互动的例子
与OP类似,但您可以控制主机,端口和消息+如果连接失败,则会出现弹出错误通知。
示例(使用AsyncTask的Android双向网络套接字)

文章图片

用法1:
  • 在局域网上获取Android和Linux桌面
  • 使用ifconfig找到桌面的IP
  • 在终端上运行netcat -l 12345
  • 在Android上,填写桌面的IP
  • 单击联系服务器
  • 在终端上,输入回复,然后点击Ctrl + D
  • 它出现在output:部分
用法2:
  • 主机名google.com
  • 港口80
  • 消息:"GET / HTTP/1.1Host: google.com"
请注意,在回复期望进一步请求后,某些HTTP服务器将不会关闭,并且应用程序将挂起直到超时。这些服务器希望你解析Content-Width标题并关闭自己。
如果连接失败,则会在对话框中向用户显示警告消息。

添加到AndroidManifest.xml
< uses-permission android:name="android.permission.INTERNET" />

主要活动是:
import android.app.Activity; import android.app.AlertDialog; import android.app.IntentService; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class Main extends Activity { final static String TAG = "AndroidCheatSocket"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final LinearLayout linearLayout = new LinearLayout(this); linearLayout.setOrientation(LinearLayout.VERTICAL); TextView textView; final String defaultHostname = "192.168.0."; textView = new TextView(this); textView.setText("hostname / IP:"); linearLayout.addView(textView); final EditText hostnameEditText = new EditText(this); hostnameEditText.setText(defaultHostname); hostnameEditText.setSingleLine(true); linearLayout.addView(hostnameEditText); textView = new TextView(this); textView.setText("port:"); linearLayout.addView(textView); final EditText portEditText = new EditText(this); portEditText.setText("12345"); portEditText.setSingleLine(true); linearLayout.addView(portEditText); textView = new TextView(this); textView.setText("data to send:"); linearLayout.addView(textView); final EditText dataEditText = new EditText(this); dataEditText.setText(String.format("GET / HTTP/1.1Host: %s", defaultHostname)); linearLayout.addView(dataEditText); final TextView replyTextView = new TextView(this); final ScrollView replyTextScrollView = new ScrollView(this); replyTextScrollView.addView(replyTextView); final Button button = new Button(this); button.setText("contact server"); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { button.setEnabled(false); new MyAsyncTask(Main.this, replyTextView, button).execute( hostnameEditText.getText().toString(), portEditText.getText().toString(), dataEditText.getText().toString()); } }); linearLayout.addView(button); textView = new TextView(this); textView.setText("output:"); linearLayout.addView(textView); linearLayout.addView(replyTextScrollView); this.setContentView(linearLayout); }private class MyAsyncTask extends AsyncTask< String, Void, String> { Activity activity; Button button; TextView textView; IOException ioException; MyAsyncTask(Activity activity, TextView textView, Button button) { super(); this.activity = activity; this.textView = textView; this.button = button; this.ioException = null; } @Override protected String doInBackground(String... params) { StringBuilder sb = new StringBuilder(); try { Socket socket = new Socket( params[0], Integer.parseInt(params[1])); OutputStream out = socket.getOutputStream(); out.write(params[2].getBytes()); InputStream in = socket.getInputStream(); byte buf[] = new byte[1024]; int nbytes; while ((nbytes = in.read(buf)) != -1) { sb.append(new String(buf, 0, nbytes)); } socket.close(); } catch(IOException e) { this.ioException = e; return "error"; } return sb.toString(); } @Override protected void onPostExecute(String result) { if (this.ioException != null) { new AlertDialog.Builder(this.activity) .setTitle("An error occurrsed") .setMessage(this.ioException.toString()) .setIcon(android.R.drawable.ic_dialog_alert) .show(); } else { this.textView.setText(result); } this.button.setEnabled(true); } } }

On GitHub with build boilerplate。
我还发布了一个Android服务器示例:https://stackoverflow.com/a/35745834/895245
在Android 5.1.1,Sony Xperia 3 D6643上测试。

    推荐阅读