Android Socket 简单介绍

这篇具有很好参考价值的文章主要介绍了Android Socket 简单介绍。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

最近需求需要使用Socket进行通讯,我在工作后的安卓开发中没有接触过,所以有了这篇文章;
写的时候想起来好像上大学的时候学过,后面一直没用忘记了;
之前公司同事的男朋友也在写这个来着,我当时还嘲笑怎么还会有人用这个,现在自己遇到了🐕;


一、Socket是什么?

百度百科的解释

所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。

套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的lP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。例如:如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)

我自己的理解

分客户端与服务端,socket以服务端的ip地址和约定好的端口号进行组合得到套接字进行一个两端的相互连接,达到能够互相收发消息的一个目的。

二、简单示例

下面的代码将演示如何在一个app中实现服务端和客户端的简单示例

1.服务端

先创建抽象的ServerCallback

interface ServerCallback {
    //接收客户端的消息
    fun receiveClientMsg(success: Boolean, msg: String)
    //其他消息,例如有客户端连接和发送消息成功等
    fun otherMsg(msg: String)
}

然后创建SocketServer来管理服务端的连接

代码如下:

import android.util.Log
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.net.ServerSocket
import java.net.Socket

object SocketServer {

    private val TAG = SocketServer::class.java.simpleName

    private const val SOCKET_PORT =
        7878

    private var socket: Socket? = null
    private var serverSocket: ServerSocket? = null

    private lateinit var mCallback: ServerCallback

    private lateinit var outputStream: OutputStream

    var result = true
    /**
     * 开启服务
     */
    fun startServer(callback: ServerCallback): Boolean {
        Log.i(TAG, "startServer: ")
        mCallback = callback
        Thread {
            try {
                serverSocket = ServerSocket(SOCKET_PORT)
                while (result) {
                    socket = serverSocket?.accept()
                    mCallback.otherMsg("${socket?.inetAddress} to connected")
                    ServerThread(socket!!, mCallback).start()
                }
            } catch (e: IOException) {
                e.printStackTrace()
                result = false
            }
        }.start()
        return result
    }

    /**
     * 关闭服务
     */
    fun stopServer() {
        Log.i(TAG, "stopServer: ")
        socket?.apply {
            shutdownInput()
            shutdownOutput()
            close()
        }
        serverSocket?.close()
    }

    /**
     * 发送到客户端
     */
    fun sendToClient(msg: String) {
        Thread {
            if (socket!!.isClosed) {
                Log.e(TAG, "sendToClient: Socket is closed")
                return@Thread
            }
            outputStream = socket!!.getOutputStream()
            try {
                outputStream.write(msg.toByteArray())
                outputStream.flush()
                mCallback.otherMsg("toClient: $msg")
                Log.d(TAG, "发送到客户端成功")
            } catch (e: IOException) {
                e.printStackTrace()
                Log.e(TAG, "向客户端发送消息失败")
            }
        }.start()
    }

    class ServerThread(private val socket: Socket, private val callback: ServerCallback) :
        Thread() {

        override fun run() {
            val inputStream: InputStream?
            try {
                inputStream = socket.getInputStream()
                val buffer = ByteArray(1024)
                var len: Int
                var receiveStr = ""
                if (inputStream.available() == 0) {
                    Log.e(TAG, "inputStream.available() == 0")
                }
                while (inputStream.read(buffer).also { len = it } != -1) {
                    receiveStr += String(buffer, 0, len, Charsets.UTF_8)
                    if (len < 1024) {
                        callback.receiveClientMsg(true, receiveStr)
                        receiveStr = ""
                    }
                }
            } catch (e: IOException) {
                e.printStackTrace()
                e.message?.let { Log.e("socket error", it) }
                callback.receiveClientMsg(false, "")
            }
        }
    }
}

可以看到我们在通过startServer方法初始化ServerSocket时,只使用到了SOCKET_PORT,这个ServerSocket方法接收一个端口号,不接收ip,因为它将作为服务端
ServerSocket方法的注释译文: 创建绑定到指定端口的服务器套接字。端口号为0表示端口号是自动分配的,通常是从临时端口范围分配的。然后可以通过调用getLocalPort来检索这个端口号。传入连接指示(连接请求)的最大队列长度设置为50。如果连接指示在队列已满时到达,则拒绝连接。

2.客户端

同样我们创建一个抽象的 ClientCallback

interface ClientCallback {
    //接收服务端的消息
    fun receiveServerMsg(msg: String)
    //其他消息
    fun otherMsg(msg: String)
}

然后创建SocketClient来管理服务端的连接

代码如下:



import android.util.Log
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.OutputStream
import java.net.Socket
import java.util.Locale

object SocketClient {

    private val TAG = SocketClient::class.java.simpleName

    private var socket: Socket? = null

    private var outputStream: OutputStream? = null

    private var inputStreamReader: InputStreamReader? = null

    private lateinit var mCallback: ClientCallback

    private const val SOCKET_PORT = 7878

    /**
     * 连接服务
     */
    fun connectServer(ipAddress: String, callback: ClientCallback) {
        mCallback = callback
        Thread {
            try {
                socket = Socket(ipAddress, SOCKET_PORT)
                ClientThread(socket!!, mCallback).start()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }.start()
    }

    /**
     * 关闭连接
     */
    fun closeConnect() {
        try {
            inputStreamReader?.close()
            outputStream?.close()
            socket?.apply {
                shutdownInput()
                shutdownOutput()
                close()
            }
            Log.d(TAG, "关闭连接")
        }catch (e: Exception){
            e.printStackTrace()
        }

    }

    /**
     * 发送数据至服务器
     * @param msg 要发送至服务器的字符串
     */
    fun sendToServer(msg: String) {
        Thread {
            if (socket!!.isClosed) {
                Log.e(TAG, "sendToServer: Socket is closed")
                return@Thread
            }
            outputStream = socket?.getOutputStream()
            try {
                outputStream?.write(msg.toByteArray())
                outputStream?.flush()
                mCallback.otherMsg("toServer: $msg")
                Log.e(TAG, "向服务端发送消息成功")
            } catch (e: IOException) {
                e.printStackTrace()
                Log.e(TAG, "向服务端发送消息失败")
            }
        }.start()
    }



    class ClientThread(private val socket: Socket, private val callback: ClientCallback) : Thread() {
        override fun run() {
            val inputStream: InputStream?
            try {
                inputStream = socket.getInputStream()
                val buffer = ByteArray(1024)
                var len: Int
                var receiveStr = ""
                if (inputStream.available() == 0) {
                    Log.e(TAG, "inputStream.available() == 0")
                }
                while (inputStream.read(buffer).also { len = it } != -1) {
                    receiveStr += String(buffer, 0, len, Charsets.UTF_8)
                    if (len < 1024) {
                        callback.receiveServerMsg(receiveStr)
                        receiveStr = ""
                    }
                }
            } catch (e: IOException) {
                e.printStackTrace()
                e.message?.let { Log.e("socket error", it) }
                callback.receiveServerMsg( "")
            }
        }
    }
}

可以看到我们在客户端通过connectServer方法连接服务端时,使用了Socket方法,它接收了一个ip和一个约定好的端口号,这里的ip就是服务端的ip
Socket方法的注释译文为: 创建流套接字并将其连接到指定主机上的指定端口号。如果指定的主机为空,则相当于指定地址为InetAddress。getByName (null)。也就是说,它相当于指定loopback接口的地址。

3.布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">


    <LinearLayout
        android:id="@+id/lay_server"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_ip_address"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp"
            android:text="Ip地址:" />

        <Button
            android:id="@+id/btn_start_service"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            android:text="开启服务"
             />
        <Button
            android:id="@+id/btn_connect_service"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            android:text="开启链接"
             />
        <Button
            android:id="@+id/btn_client_send_msg"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            android:text="客户端发送消息"
            />
        <Button
            android:id="@+id/btn_server_send_msg"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            android:text="服务端发送消息"
            />
    </LinearLayout>

</LinearLayout>

4.实现

在activity中

import android.net.wifi.WifiManager
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.zyf.vlc.databinding.ActivityMainBinding
import com.zyf.vlc.socket.ClientCallback
import com.zyf.vlc.socket.ServerCallback
import com.zyf.vlc.socket.SocketClient
import com.zyf.vlc.socket.SocketServer


class MainActivity : AppCompatActivity(),ServerCallback, ClientCallback {

    private  val TAG = "MainActivity"
    private lateinit var binding: ActivityMainBinding

    //Socket服务是否打开
    private var openSocket = false

    private var connectSocket = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)


        binding.tvIpAddress.text = "Ip地址:${getIp()}"

        //开启服务/关闭服务 服务端处理
        binding.btnStartService.setOnClickListener {
            openSocket = if (openSocket) {
                SocketServer.stopServer();false
            } else SocketServer.startServer(this)

            //改变按钮文字
            binding.btnStartService.text = if (openSocket) "关闭服务" else "开启服务"
        }

        binding.btnConnectService.setOnClickListener {
            val ip = getIp()
            connectSocket = if (connectSocket) {
                SocketClient.closeConnect();false
            } else {
                SocketClient.connectServer(ip, this);true
            }

            binding.btnConnectService.text = if (connectSocket) "关闭连接" else "连接服务"
        }

        binding.btnClientSendMsg.setOnClickListener {
            SocketClient.sendToServer("客户端发送消息")
        }


        binding.btnServerSendMsg.setOnClickListener {
            SocketServer.sendToClient("服务端发送消息")
        }


 private fun getIp() =
        intToIp((applicationContext.getSystemService(WIFI_SERVICE) as WifiManager).connectionInfo.ipAddress)

    private fun intToIp(ip: Int) =
        "${(ip and 0xFF)}.${(ip shr 8 and 0xFF)}.${(ip shr 16 and 0xFF)}.${(ip shr 24 and 0xFF)}"



    override fun receiveClientMsg(success: Boolean, msg: String) {
        Log.i(TAG, "receiveClientMsg: $msg")
    }

    override fun receiveServerMsg(msg: String) {
        Log.i(TAG, "receiveServerMsg: $msg")
    }

    override fun otherMsg(msg: String) {
        Log.i(TAG, "otherMsg: $msg")
    }

最终实现的效果如下

android socket,Android基础,android
运行后依次点击按钮,可以得到如下日志
android socket,Android基础,android


参考

Android Socket通讯

总结

本文简单介绍了Android 中Socket 的使用方法,并通过简单的示例帮助理解。
可以关注下我的公众号,我会不定时分享文章和帮助解决问题文章来源地址https://www.toymoban.com/news/detail-719216.html

到了这里,关于Android Socket 简单介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android studio socket客户端应用设计

    一、XML布局设计:

    2024年02月03日
    浏览(42)
  • Android Socket使用TCP协议实现手机投屏

    本节主要通过实战来了解Socket在TCP/IP协议中充当的是一个什么角色,有什么作用。通过Socket使用TCP协议实现局域网内手机A充当服务端,手机B充当客户端,手机B连接手机A,手机A获取屏幕数据转化为Bitmap,通过Socket传递个手机B显示。 实现效果: Socket 是应用层与TCP/IP协议族通

    2024年02月13日
    浏览(42)
  • 【Android车载系列】第9章 车载通信-Socket实现IPC通信机制(实现仿FDBus效果)

      FDBus 基于 Socket (TCP 和 Unix domain) 之上的IPC机制, 采用 Google protobuf 做序列化和反序列化。 FDBus还支持字符串形式的名字作为server地址。通过 name server 自动为 server 分配Unix domain 地址和 TCP 端口号, 实现 client 和server 之间用服务名字寻址。 一句话描述:FDBus (Fast Distributed Bus

    2024年02月10日
    浏览(55)
  • Python网络编程(一)——了解IP和端口的基础知识以及socket的简单实现

    Python网络编程(一)——了解IP和端口的基础知识以及socket的简单实现 IP(Internet Protocol) 地址是唯一标识互联网上连接至计算机或其他设备的地址。每一个设备在 IP 网络中拥有一个不同的 IP 地址,它由 32 位二进制数组成,通常表示为四个从 0 到 255 的十进制数之间用 (.)

    2024年02月04日
    浏览(59)
  • windows Socket简单编程实例

    2024年02月12日
    浏览(35)
  • C++ Windows Socket 简单示例

    2024年02月09日
    浏览(33)
  • 网络编程之简单socket通信

    Socket,又叫套接字,是在应用层和传输层的一个抽象层。它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。  socket分为流socket和数据报socket,分别基于tcp和udp实现。 SOCK_STREAM 有以下几个特征: 数据在传输过程中不会消失; 数据是按照顺序

    2024年02月01日
    浏览(46)
  • Socket简要介绍

    简介 Socket作为计算机术语翻译为“套接字”,而它更常见的含义是:插座。 Socket就像一个电话插座,负责连通两端的电话,进行点对点通信,让电话可以进行通信,端口就像插座上的孔,端口不能同时被其他进程占用。而我们建立连接就像把插头插在这个插座上,创建一个

    2024年02月06日
    浏览(46)
  • Socket的详细介绍

    本文是关于Socket通信的大杂烩。我深知自己的局限性和不足之处,无论是在语言表达、逻辑思维还是知识储备上,都有许多需要改进和学习的地方。因此,如果您发现我在博客中有任何错误、不准确或者需要补充的地方,欢迎指正并提出建议。 Socket(套接字)是一种用于实现

    2024年02月09日
    浏览(23)
  • linux Socket 缓存 介绍

    这里介绍的成员是驱动可能需要存取的. 以非特别的顺序列出它们. struct net_device *dev; 接收或发送这个缓存的设备 union { /* ... */ } h; union { /* ... */ } nh; union { /*... */} mac; 指向报文中包含的各级的头的指针. union 中的某个成员都是一个不同数据结构类 型的指针. h 含有传输层头部指

    2024年01月25日
    浏览(18)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包