【小沐学Android】Android实现socket网络通信

15 篇文章 6 订阅

🍺SOCKET网络通信系列文章链接如下:🍺
🎈【小沐学python】(一)Python简介和安装🎈
🎈Python实现socket网络通信🎈
🎈C++实现socket网络通信(Windows)🎈
🎈Android实现socket网络通信🎈
🎈nodejs实现socket网络通信🎈

在这里插入图片描述

1、简介

1.1 socket流程

Android在包java.net包下提供了ServerSocket和Socket类。当连接成功,客户端和服务端都会产生一个Socket实例,通过此Socket进行通信。

1.2 需要在子线程运行

直接在主线程中调用相关网络接口会报错:
Caused by: android.os.NetworkOnMainThreadException

从Android4.0以后,就不支持在主线程中直接初始化socket了。
所以当进行网络请求的时候 还是需要开辟一个子线程,然后等到数据返回成功后再刷新UI。

为了避免创建无响应的界面,请勿在主线程上执行网络操作。默认情况下,Android 要求在非主界面线程上执行网络操作;否则将抛出 NetworkOnMainThreadException。

The exception that is thrown when an application attempts to perform a
networking operation on its main thread.

This is only thrown for applications targeting the Honeycomb SDK or
higher. Applications targeting earlier SDK versions are allowed to do
networking on their main event loop threads, but it’s heavily
discouraged.

https://developer.android.com/reference/android/os/NetworkOnMainThreadException

1.3 需要的权限

若要在您的应用中执行网络操作,您的清单必须包含以下权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

注意:Internet 和 ACCESS_NETWORK_STATE 权限都是普通权限,这意味着这些权限会在安装时授予,不需要在运行时请求。

2、环境配置

2.1 Android Studio下载

Download Android Studio and SDK tools | Android Developers
​​在这里插入图片描述

2.2 真机无线调试

  1. 将手机与电脑连接在同一局域网内
    电脑端开放热点,手机连上电脑端的热点,即保证手机与电脑在同一局域网内(或者手机给电脑开热点)。

  2. 手机与电脑通过USB线连接
    手机端:“开发者选项” – “打开USB调试” –“打开 连接USB时总是弹出提示”-- ”选择USB配置–MTP“
    AndroidStudio中 能看到连接的设备即代表USB线连接成功。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3. Android Studio中"Terminal窗口"或者"cmd窗口",依次输入命令,实现无线连接。(此处以Terminal窗口为例)
每台电脑的adb.exe的安装路径都不相同,比如

D:\Microsoft\AndroidSdk\platform-tools\adb.exe

  • 命令1:
    adb devices
    在这里插入图片描述

  • 命令2
    adb -s <设备号> tcpip <端口号>
    或者
    adb tcpip <端口号>
    在这里插入图片描述

  • 命令3
    adb connect <手机IP地址:8888>
    在这里插入图片描述

  • 命令4
    adb disconnect <手机IP地址>:8888

  • 其他命令
    执行adb kill-server 再执行 adb start-server,重启adb。

  1. 通过以上步骤,到AndroidStudio中,看到有两个设备连接,就代表无线连接成功了。
    在这里插入图片描述

  2. 此时拔掉USB数据线,看到上面一个设备名。
    在这里插入图片描述

  3. 测试一下,无线真机调试通道是否已经打通。
    在这里插入图片描述

3、TCP方式

3.1 服务端

MyServerSocket.java如下:

package com.fxyublib.android.fxappsockettest;

import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerSocket extends Thread{
    private final static String TAG = "MyServerSocket";
    private ServerSocket mServerSocket = null;
    private Socket mSocket = null;
    private InputStream mInStream = null;
    private OutputStream mOutStream = null;
    private Boolean isConnected = false;
    private int mPort = 12345;
    private Handler mHandler = null;

    MyServerSocket(int port, Handler handler) {
        mPort = port;
        mHandler = handler;
    }

    @Override
    public void run() {
        System.out.println("MyServerSocket: " + Thread.currentThread().getName());

        try {
            mServerSocket = new ServerSocket(mPort);

            //等待客户端的连接,Accept会阻塞,直到建立连接,
            //所以需要放在子线程中运行。
            Log.d(TAG, "run: start0");
            mSocket = mServerSocket.accept();
            Log.d(TAG, "run: start1");

            //获取输入流
            mInStream = mSocket.getInputStream();
            //获取输出流
            mOutStream = mSocket.getOutputStream();

            isConnected = true;
            writeMsgInternal("### A message from MyServerSocket.");
            readMsgInternal();

        } catch (IOException e) {
            e.printStackTrace();
            mHandler.sendEmptyMessage(MyEnum.MSG_SOCKET_ACCEPTFAIL);
            isConnected = false;
            Log.d(TAG, "run: err");
        }
        //Log.i(TAG, "accept success");
        mHandler.sendEmptyMessage(MyEnum.MSG_SOCKET_CONNECTOK);
        Log.d(TAG, "run: end");
    }

    private void readMsgInternal() {
        while (isConnected) {
            byte[] buffer = new byte[1024];
            //循环执行read,用来接收数据。
            //数据存在buffer中,count为读取到的数据长度。
            try {
                Log.d(TAG, "readMsgInternal:start0");
                int count = mInStream.read(buffer);
                String str = new String(buffer, "UTF-8");
                Log.d(TAG, "readMsgInternal:start1");

                Message msg = new Message();
                msg.what = MyEnum.MSG_SOCKET_READ;
                msg.obj = str;
                if(mHandler != null) mHandler.sendMessage ( msg );
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "readMsgInternal:err");
                isConnected = false;
            }
        }

        Log.d(TAG, "readMsgInternal:end");
    }

    private void writeMsgInternal(String msg){
        if(msg.length() == 0 || mOutStream == null)
            return;

        try {   //发送
            mOutStream.write(msg.getBytes());
            mOutStream.flush();
        }catch (Exception e) {
            e.printStackTrace();
            isConnected = false;
        }
    }

    public void writeMsg(String str) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                writeMsgInternal(str);
            }
        }).start();
    }

    public void close() {
        isConnected = false;

        if(mServerSocket != null){
            try {
                mServerSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            mServerSocket = null;
        }

        if(mSocket != null) {
            try {
                mSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            mSocket = null;
        }
    }
}

3.2 客户端

MyClientSocket.java如下:

package com.fxyublib.android.fxappsockettest;

import android.os.Handler;
import android.os.Message;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class MyClientSocket extends Thread{

    private Socket mSocket = null;
    private InputStream mInStream = null;
    private OutputStream mOutStream = null;
    private Boolean isConnected = false;
    private String mIP;
    private int mPort = 12345;
    private Handler mHandler = null;

    MyClientSocket(String ip, int port, Handler handler) {
        mIP = ip;
        mPort = port;
        mHandler = handler;
    }

    public void run(){
        System.out.println("MyClientSocket: " + Thread.currentThread().getName());

        try {
            //指定ip地址和端口号
            mSocket = new Socket(mIP, mPort);

            //获取输出流、输入流
            mOutStream = mSocket.getOutputStream();
            mInStream = mSocket.getInputStream();

            isConnected = true;
            writeMsgInternal("### A message from MyClientSocket.");
            readMsgInternal();

        } catch (Exception e) {
            e.printStackTrace();
            mHandler.sendEmptyMessage(MyEnum.MSG_SOCKET_CONNECTFAIL);
            isConnected = false;
            return;
        }

        mHandler.sendEmptyMessage(MyEnum.MSG_SOCKET_CONNECTOK);
    }

    private void readMsgInternal() {
        while (isConnected) {
            byte[] buffer = new byte[1024];
            //循环执行read,用来接收数据。
            //数据存在buffer中,count为读取到的数据长度。
            try {
                int count = mInStream.read(buffer);
                String str = new String(buffer, "UTF-8");

                Message msg = new Message();
                msg.what = MyEnum.MSG_SOCKET_READ;
                msg.obj = str;
                if(mHandler != null) mHandler.sendMessage ( msg );
            } catch (IOException e) {
                e.printStackTrace();
                isConnected = false;
            }
        }
    }

    private void writeMsgInternal(String msg){
        if(msg.length() == 0 || mOutStream == null)
            return;
        try {   //发送
            mOutStream.write(msg.getBytes());
            mOutStream.flush();
        }catch (Exception e) {
            e.printStackTrace();
            isConnected = false;
        }
    }

    public void writeMsg(String str) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                writeMsgInternal(str);
            }
        }).start();
    }

    public void close(){
        isConnected = false;

        try {
            if (mOutStream != null) {
                mOutStream.close();
                mOutStream = null;
            }
            if (mInStream != null) {
                mInStream.close();
                mInStream = null;
            }
            if(mSocket != null){
                mSocket.close();
                mSocket = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.3 运行结果

  • Android的TCP服务器和客户端的界面如下:
    在这里插入图片描述
    窗口界面布局文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    tools:context=".FirstFragment">

    <TextView
        android:id="@+id/textview_first"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_first_fragment"
        android:minHeight="32dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/ll_ip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@id/textview_first">

        <EditText
            android:id="@+id/edit_ip"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="textPersonName"
            android:minHeight="64dp"
            android:hint = "IP地址"
            android:text="192.168.31.223"
            android:singleLine="true"
            android:layout_weight="1.0"
            android:importantForAutofill="no" />

        <EditText
            android:id="@+id/edit_port"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="number"
            android:minHeight="64dp"
            android:hint = "端口号"
            android:text="12345"
            android:singleLine="true"
            android:layout_weight="3"
            android:importantForAutofill="no" />

    </LinearLayout>

    <RadioGroup
        android:id="@+id/ll_tcpudp"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@id/ll_ip">

        <RadioButton
            android:id="@+id/radio_tcp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TCP"
            android:layout_weight="1" />

        <RadioButton
            android:id="@+id/radio_udp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="UDP"
            android:layout_weight="1" />

    </RadioGroup>

    <RadioGroup
        android:id="@+id/ll_serverclient"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@id/ll_tcpudp">

        <RadioButton
            android:id="@+id/radio_server"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Server" />

        <RadioButton
            android:id="@+id/radio_client"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Client" />
    </RadioGroup>

    <EditText
        android:id="@+id/edit_send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/ll_serverclient"
        android:ems="10"
        android:minLines="2"
        android:inputType="textMultiLine"
        tools:ignore="MissingConstraints" />

    <LinearLayout
        android:id="@+id/ll_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@id/edit_send">

        <Button
            android:id="@+id/button_connect"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:insetLeft="32dp"
            android:insetRight="32dp"
            android:text="Connect" />

        <Button
            android:id="@+id/button_send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send"
            android:insetLeft="32dp"
            android:insetRight="32dp"
            android:layout_weight="1"/>

    </LinearLayout>

    <TextView
        android:id="@+id/textView_log"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/ll_button"
        android:text="" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • 对应测试的Python服务端:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# !/usr/bin/env python
# coding=utf-8
# filename:test_tcp_server.py
# creator: tomcat
# date: 2021-11-16

from socket import *
from time import ctime
from datetime import datetime


IP = gethostbyname(gethostname())
print("本机IP: ", IP)
ADDR = (IP, 12345)
BUFSIZ = 1024

s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(5)

while True:
    print('waiting for clients ...')
    c, addr = s.accept()

    t = datetime.now ().strftime ('%H:%M:%S')
    print('   connnecting from: [', t, "]", addr)

    while True:
        data = c.recv(BUFSIZ)
        if not data:
            break

        t = datetime.now().strftime('%H:%M:%S')
        print("recv: ", t, data )

        c.send(('[%s] %s' % (ctime(), "A message from python server.")).encode())
    c.close()

s.close()
  • 对应测试的Python客户端:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# filename:test_tcp_client.py
# creator: tomcat
# date: 2021-11-16

from socket import *
from time import ctime
from datetime import datetime

BUFSIZ = 1024
ADDR = ('192.168.31.87', 12345) # or 'localhost' 127.0.0.1

c = socket(AF_INET, SOCK_STREAM)
c.connect(ADDR)
while True:
    #data = input('Input: ')
    #if not data:
    #    break
    #c.send(data.encode())
    data = c.recv(BUFSIZ)
    if not data:
        break

    t = datetime.now().strftime('%H:%M:%S')
    print("[", t,"]",data.decode('utf-8'))
    c.send(t.encode())
print("End!");
c.close()

后记

如果你觉得该方法或代码有一点点用处,可以给作者点个赞;╮( ̄▽ ̄)╭
如果你感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进。o_O???
谢谢各位童鞋们啦( ´ ▽´ )ノ ( ´ ▽´)っ!!!

  • 23
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
Socket通信在Android上是通过Socket API来实现的。Socket是一种抽象层,应用程序通过它来发送和接收数据,使用Socket可以将应用程序添加到网络中,与处于同一网络中的其他应用程序进行通信。简单来说,Socket提供了程序内部与外界通信的端口并为通信双方提供了数据传输通道。Android中的Socket通信主要使用TCP/IP协议进行开发。通过建立Socket连接,Android应用程序可以与服务器进行数据交互,并实现信息的主动推送,而不需要每次由客户端向服务器发送请求。Socket通信主要有两种方式:Http通信和Socket通信。Http通信使用的是“请求-响应方式”,即客户端在请求时建立连接通道,服务器端才能向客户端返回数据。而Socket通信则是在双方建立起连接后就可以直接进行数据的传输。Socket通信具有数据丢失率低、使用简单且易于移植的特点。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Android Socket通讯](https://blog.csdn.net/qq_38436214/article/details/126177462)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)](https://blog.csdn.net/mad1989/article/details/9147661)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值