如何在 Android 上建立套接字连接

Posted

技术标签:

【中文标题】如何在 Android 上建立套接字连接【英文标题】:How to make socket connection on Android 【发布时间】:2017-02-07 01:19:46 【问题描述】:

我正在尝试制作一个简单的应用程序,它可以发送来自EditText 的消息, 使用 Java Socket 类。我正在尝试使用AsyncTask,但它只工作一次,我无法返回套接字以在该类的另一个实例中重用。

你能给我一个后台服务的例子吗?它打开与服务器的通信并返回套接字?

编辑:nandsito 要求;我打算使用Button 打开一个连接,所以这个按钮调用一个后台进程来创建与服务器的连接,最后返回Socket。当我按下另一个 Button 时,我想启动另一个重用套接字的任务,写入数据(例如 Sring)接收来自服务器的响应并更新 UI。

【问题讨论】:

我更新了答案,请看一下 【参考方案1】:

看起来很简单,但我认为您遇到了一个有趣且具有挑战性的问题。如果您想在通过它发送消息后保持套接字打开,则需要维护一个或多个线程来使用该套接字,因为您知道,android 不允许在主线程上联网。

多线程编程很少是简单的,而且通常有不止一种方法可以做到这一点。例如。在 Android 中,您可以将 Handlers 与来自 HandlerThreads 的 Loopers 或经典的 Java Thread 一起使用。还有AsyncTask,但我认为它不适合这种情况。

您打算如何管理套接字生命周期(即它何时打开或关闭),以及数据在哪些时刻从套接字读取/写入/写入套接字?请更好地解释此事,以便我提出实施建议。

编辑

这是一个带有两个按钮的示例Activity。一个按钮运行AsyncTask 创建套接字及其流,另一个按钮运行另一个AsyncTask 将数据写入套接字。这是一个过于简单的解决方案,但它应该可以工作。请注意,代码需要同步,以便不同的线程访问套接字。

public class MainActivity extends Activity 

    private SocketContainer mSocketContainer;
    private final Object mSocketContainerLock = new Object();

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    

    // onClick attribute of one button.
    public void onClickPushMe(View view) 
        String serverAddress;
        int serverPort;
        new CreateSocketAsyncTask(serverAddress, serverPort).execute();
    

    // onClick attribute of other button.
    public void onClickPushMeToo(View view) 
        String text;
        new WriteSocketAsyncTask(text).execute();
    

    // Class that contains the socket and its streams,
    // so they can be passed from one thread to another.
    private class SocketContainer 

        private Socket mSocket;
        private InputStream mSocketInputStream;
        private OutputStream mSocketOutputStream;

        private SocketContainer(Socket socket, InputStream socketInputStream, OutputStream socketOutputStream) 
            mSocket = socket;
            mSocketInputStream = socketInputStream;
            mSocketOutputStream = socketOutputStream;
        

        private Socket getSocket() 
            return mSocket;
        

        private InputStream getSocketInputStream() 
            return mSocketInputStream;
        

        private OutputStream getSocketOutputStream() 
            return mSocketOutputStream;
        
    

    // AsyncTask that creates a SocketContainer and sets in into MainActivity.
    private class CreateSocketAsyncTask extends AsyncTask<Void, Void, SocketContainer> 

        private final String mServerAddress;
        private final int mServerPort;

        private CreateSocketAsyncTask(String serverAddress, int serverPort) 
            mServerAddress = serverAddress;
            mServerPort = serverPort;
        

        protected SocketContainer doInBackground(Void... params) 
            try 
                Socket socket = new Socket(mServerAddress, mServerPort);
                return new SocketContainer(socket, socket.getInputStream(), socket.getOutputStream());
             catch (IOException e) 
                throw new RuntimeException(e);
            
        

        @Override
        protected void onPostExecute(SocketContainer socketContainer) 
            super.onPostExecute(socketContainer);
            synchronized (mSocketContainerLock) 
                mSocketContainer = socketContainer;
            
        
    

    private class WriteSocketAsyncTask extends AsyncTask<Void, Void, Void> 

        private final String mText;

        private WriteSocketAsyncTask(String text) 
            mText = text;
        

        @Override
        protected Void doInBackground(Void... params) 
            synchronized (mSocketContainerLock) 
                try 
                    mSocketContainer.getSocketOutputStream().write(mText.getBytes(Charset.forName("UTF-8")));
                    mSocketContainer.getSocketOutputStream().flush();
                 catch (IOException e) 
                    throw new RuntimeException(e);
                
            
            return null;
        
    

【讨论】:

同步的 (mSocketContainerLock) 无法解析 mSocketContainerLock @GiuseppeCesaranoPecoraInPann 你是使用了我提供的完全相同的代码还是你将内部类分解成单独的类? @nandsito 你说得对,我肢解了内部类,可以在不同的类文件中进行这项工作吗? @GiuseppeCesaranoPecoraInPann 您可以为mSocketContainerLock 放置一个getter,为mSocketContainer 放置一个getter 和setter,并将MainActivity 实例传递给AsyncTasks 构造函数,这样您就可以执行@ 之类的操作987654335@ 但是(1)这个解决方案会变得非常复杂,(2)它可能会泄漏Context:android-developers.blogspot.com.br/2009/01/…【参考方案2】:

使用此代码,我可以连接到聊天室,因此您可以使用它来连接您想要的内容

 public class SocialConnectionManager extends AsyncTask<Void, Void, Void> 

    public static final int SQL_STEP_LOGIN = 0;
    public static final int SQL_STEP_LOGOUT = 1;
    public static final int SQL_STEP_SEND = 2;
    public static final int SQL_STEP_UPDATE = 3;

    final int serverPort = 8080;
    private String message, channel, userName, serverIp;
    private int step;
    private long uniqueId;
    private Activity activity;

    public SocialConnectionManager(String serverIp, long uniqueId, int step, String userName,
                                   String channel, String message, Activity activity) 
        this.message = message;
        this.step = step;
        this.uniqueId = uniqueId;
        this.channel = channel;
        this.userName = userName;
        this.serverIp = serverIp;
        this.activity = activity;
    

    @Override
    protected Void doInBackground(Void... arg0) 

        Socket socket = null;

        try 
            socket = new Socket(serverIp, serverPort);
            DataOutputStream dataOut = new DataOutputStream(socket.getOutputStream());
            switch (step) 
                case SQL_STEP_LOGIN:
                    dataOut.writeInt(step);
                    dataOut.writeLong(uniqueId);
                    dataOut.writeUTF(channel);
                    dataOut.writeUTF(userName);
                    break;
                case SQL_STEP_LOGOUT:
                    dataOut.writeInt(step);
                    dataOut.writeLong(uniqueId);
                    dataOut.writeUTF(channel);
                    dataOut.writeUTF(userName);
                    break;
                case SQL_STEP_SEND:
                    long messageId = createRandomId();
                    messageIds.add(messageId);
                    dataOut.writeInt(step);
                    dataOut.writeLong(uniqueId);
                    dataOut.writeUTF(channel);
                    dataOut.writeUTF(userName);
                    dataOut.writeUTF(message);
                    dataOut.writeLong(messageId);
                    break;
                case SQL_STEP_UPDATE:
                    dataOut.writeInt(step);
                    dataOut.writeUTF(message);
                    break;
            
            dataOut.flush();

         catch (UnknownHostException e) 
            activity.runOnUiThread(new Runnable() 
                @Override
                public void run() 
                    ((MainActivity) activity).showNetworkAlertDialog(context.getString
                            (R.string.social_chat_connection_failed));
                
            );
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         finally 
            if (socket != null) 
                try 
                    socket.close();
                 catch (IOException e) 
                    e.printStackTrace();
                
            
        
        return null;
    

    @Override
    protected void onPostExecute(Void result) 
        super.onPostExecute(result);
    


private class ReceiveTask extends AsyncTask 

    final int clientPort = 5050;

    @Override
    protected Object doInBackground(Object[] params) 
        try 
            serverSocket = new ServerSocket(clientPort);
            while (true) 
                final Socket socket = serverSocket.accept();
                DataInputStream dataIn = new DataInputStream(socket.getInputStream());
                final int step = dataIn.readInt();
                final int userCount = dataIn.readInt();
                final String message = dataIn.readUTF();
                final String userName = dataIn.readUTF();
                switch (step) 
                    case SocialConnectionManager.SQL_STEP_LOGIN:
                        if (isLogging) 
                            activity.runOnUiThread(new Runnable() 
                                @Override
                                public void run() 
                                    showProgress(false);
                                
                            );
                            isLogging = false;
                            isLoggedIn = true;
                        
                        activity.runOnUiThread(new Runnable() 
                            @Override
                            public void run() 
                                userCountView.setText(Integer.toString(userCount));
                                addMessage(message, userName, step);
                            
                        );
                        break;
                    case SocialConnectionManager.SQL_STEP_LOGOUT:
                        activity.runOnUiThread(new Runnable() 
                            @Override
                            public void run() 
                                addMessage(message, userName, step);
                            
                        );
                        break;
                    case SocialConnectionManager.SQL_STEP_SEND:
                        messageId = dataIn.readLong();
                        activity.runOnUiThread(new Runnable() 
                            @Override
                            public void run() 
                                addMessage(message, userName, step);
                            
                        );
                        break;
                
            
         catch (IOException e) 
            e.printStackTrace();
        
        return null;
    


BroadcastReceiver networkStateReceiver = new BroadcastReceiver() 
    @Override
    public void onReceive(Context context, Intent intent) 
        String ip = getIpAddress();
        if (ip.equals("")) 
            ((MainActivity) activity).showNetworkAlertDialog(context.getString
                    (R.string.social_chat_connection_lost));
         else if (!deviceIp.equals(ip)) 
            SocialConnectionManager socialConnectionManager =
                    new SocialConnectionManager(serverIp, 0,
                            SocialConnectionManager.SQL_STEP_UPDATE, null, null, deviceIp,
                            activity);
            socialConnectionManager.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        
    
;

【讨论】:

【参考方案3】:

异步任务不适合实时聊天。 进入firebase以轻松使用这些东西。 这可能会帮助你- https://www.firebase.com/docs/android/examples.html

【讨论】:

以上是关于如何在 Android 上建立套接字连接的主要内容,如果未能解决你的问题,请参考以下文章

java.io.IOException: 读取失败,套接字可能关闭或超时,在 Android 5.0.1 Lollipop 版本上读取 ret: -1

如何使用wifi直接将套接字与android连接

如何将数据从 Android 中的数据报套接字发送到 Node js 服务器?

建立多个连接时如何在C中设置套接字超时?

如何使用命令行建立套接字连接

如何将java套接字连接到远程服务器套接字