精华内容
下载资源
问答
  • 下面是自己理解rpc定义,若有不对,望指出:rpc官方称为 远程过程调用 。我这里理解为远程函数调用,即一个本机程序调用另一个机器程序中某个函数。因不是同一机器调用,故需要远程访问操作。与远程过程调用...

    grpc介绍

    grpc是谷歌开源的一套基于rpc实现的通讯框架(官网有更完整的定义)。在搞懂grpc之前,首先要弄懂rpc是什么。下面是自己理解的rpc定义,若有不对,望指出:

    rpc官方称为 远程过程调用 。我这里理解为远程函数调用,即一个本机程序调用另一个机器的程序中的某个函数。因不是同一机器调用,故需要远程访问操作。

    与远程过程调用相反的则是“近程过程调用”(哈哈,自己乱起的)。其实就是实现和调用都在同一个机器的程序中。比如,学过面向对象语言的(如java)可以解释为:一个类中实现了一个方法,然后另一个程序中new了一个这个类的事例(对象),并调用该方法。而远程过程调用则相当于一个机器中实现了一个类的方法,另一个机器new了这个类的对象,它若想要调用该方法,必须要与实现了类方法的机器进行通讯。此时我们可以称实现了类方法的机器为服务端,new了对象的机器为客户端。

    grpc通信方式

    grpc同http通讯一样,也是基于“请求响应”模式的一种通讯。客户端请求,服务器响应。关于grpc的更多介绍可以参考官网。下面说一下grpc的四种通信方式[见官网],根据不同业务场景,可以分为:

    1. 客户端单次请求,服务端回应一次:

    // Obtains the feature at a given position.

    rpc GetFeature(Point) returns (Feature) {}

    2. 客户端一次请求,服务端流式应答(其实相当于返回给客户端多条数据)

    // Obtains the Features available within the given Rectangle. Results are

    // streamed rather than returned at once (e.g. in a response message with a

    // repeated field), as the rectangle may cover a large area and contain a

    // huge number of features.

    rpc ListFeatures(Rectangle) returns (stream Feature) {}

    3. 客户端流式请求,服务端回应一次

    // Accepts a stream of Points on a route being traversed, returning a

    // RouteSummary when traversal is completed.

    rpc RecordRoute(stream Point) returns (RouteSummary) {}

    4. 客户端流式请求,服务端流式应答

    // Accepts a stream of RouteNotes sent while a route is being traversed,

    // while receiving other RouteNotes (e.g. from other users).

    rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

    知道了四种通信方式后,回到主要问题上,我们要利用这四种通信方式来实现客户端与服务端互相通信。要实现互相通信,我这里想到的有两种:

    客户端与服务端各自既是客户端又是服务端

    这种方式感觉是最容易实现的。即在客户端与服务端之间各实现一套“请求响应模型”,这样客户端主动通信服务端时是正常请求响应,服务端主动通信客户端时它此时就变为客户端来请求。这样在外部看来两个机器之间就能互相通信了。

    该种实现方式建立了两个通道来通信。缺点是要实现两套通信代码。

    客户端与服务端直接互相通信

    我们已经知道grpc是基于请求响应的,客户端请求,服务端响应。那怎么让服务端主动请求客户端通信呢? 其实我们可以用grpc的 第2或第4种的服务端流式响应 。原理是可以让客户端先发一个空消息给服务端让服务端知道(相当于客户端在服务端注册),然后服务端流式回应。因流式回应不会一下子都回完,我们可以在中途把服务端要发给客户端的消息加入到流中,让流把消息捎回到客户端。

    在外部来看客户端与服务端能互相通信了。不过这种缺点是把互相通信的业务都糅杂到一块了。

    具体实现

    上面说了两种互相通信的实现方法及grpc的四种通信方式。这里采用第二种实现方法及grpc的第二种通信方式来实现,编程语言采用Python实现。

    grpc采用protobuf来定义和传输数据。故通信的数据是用proto文件来定义的。关于proto的语法可以参考文档

    首先建立如下的目录:

    │ contact_client.py

    │ contact_server.py

    |

    ├─contact

    │ │ contact.proto

    │ │

    │ │ __init__.py

    contact.proto:定义通信的数据部分

    contact_client.py:客户端代码

    contact_server.py:服务端代码

    contact.proto内容如下:

    syntax = "proto3";

    // 定义一个服务

    service Contact {

    // 客户端通信给服务端,通信方式可以随意选择,这里我选择第4种通信方式

    rpc sendStatus (stream ClientMsg) returns (stream Result);

    // 客户端发送一个空消息给服务端,服务端就能给客户端通信了

    rpc getTask (Empty) returns (stream ServerMsg);

    // 客户端接受完服务端消息处理完后,再告诉服务端。这个tellResult也可以不要,看具体需求

    rpc tellResult (stream Result) returns (Empty);

    }

    message ClientMsg {

    string msg = 1;

    }

    message ServerMsg {

    string task = 1;

    }

    message Empty {

    }

    message Result {

    string ret = 1;

    }

    在contact文件夹下运行命令:

    python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. contact.proto

    会在contact目录下自动生成contact_pb2.py和contact_pb2_grpc.py两个文件。下来就是实现具体的通信了,首先是客户端向服务端发消息:

    contact_server.py中代码实现具体代码:

    # 注意服务端的具体实现函数是在类里面

    def sendStatus(self, request_iterator, context):

    for note in request_iterator:

    yield contact_pb2.Result(

    result=f"服务端接收到消息:{note.msg}"

    )

    contact_client.py中的代码为:

    # 先制造一些客户端能发送的数据

    def make_some_data():

    for i in range(15):

    num = random.randint(1, 20)

    yield contact_pb2.ClientMsg(msg=f"数据:{num}")

    def send_status(stub):

    try:

    while True:

    status_response = stub.sendStatus(make_some_data())

    for ret in status_response:

    print(ret.result)

    time.sleep(60)

    except Exception as e:

    print(f'err in send_status:{e}')

    return

    上面的代码就实现了客户端主动通信服务端的功能。可以看到是服务端先实现具体的代码,然后客护端调用具体函数与服务端通信,最后再对服务端返回的数据进行处理。

    而服务端主动通信客户端的方式可以理解为:客户端先给服务端发送一个消息,告诉服务端我在线,然后服务端就能发数据给客户端了,最后客户端再通知服务端我接收了你的哪些数据。具体代码为:

    server端代码:

    import logging

    import random

    import time

    from concurrent import futures

    import grpc

    from contact import contact_pb2_grpc

    from contact import contact_pb2

    # 在类初试化的时候定义了一个列表self.tasks来充当任务队列

    def getTask(self, request_iterator, context):

    print("服务端已接收到客户端上线通知,开始发送任务给客户端\n")

    last_index = 0

    while True:

    print("服务端开始发送任务给客户端了。。。。。。\n")

    while len(self.tasks) > last_index:

    n = self.tasks[last_index]

    last_index += 1

    yield n

    print(f'服务端发送给了客户端任务:{n.task}##########\n')

    # 顺便制造些服务端的任务数据用来填充到任务队列里面

    for i in range(10):

    num = random.randint(100, 200)

    self.tasks.append(contact_pb2.ServerMsg(

    task=f"任务:{num}"

    ))

    time.sleep(40)

    def tellResult(self, request_iterator, context):

    for response in request_iterator:

    print(f"我已经知道客户端接收到我发过去的任务:{response.ret}")

    return contact_pb2.Empty()

    client端代码

    import logging

    import random

    import threading

    import time

    import grpc

    from contact import contact_pb2

    from contact import contact_pb2_grpc

    # 接收服务端发送过来的任务

    def get_task(stub):

    try:

    for task in stub.getTask(contact_pb2.Empty()):

    print(f"客户端已接收到服务端任务:{task.task}\n")

    # 顺便再告诉服务端我已经接收到你发的任务,你不用担心我没接收到它

    yield contact_pb2.Result(

    ret=f"客户端接收到任务:{task.task}"

    )

    except Exception as e:

    print(f'err:{e}')

    return

    # 客户端再通知服务端我接收到你的消息了

    def tell_result(stub):

    result = get_task(stub)

    stub.tellResult(result)

    def run():

    with grpc.insecure_channel('localhost:50051') as channel:

    stub = contact_pb2_grpc.ContactStub(channel)

    while True:

    try:

    threading.Thread(target=send_status, args=(stub,), daemon=True).start()

    tell_result(stub)

    except grpc.RpcError as e:

    print(f"server connected out, please retry:{e.code()},{e.details()}")

    except Exception as e:

    print(f'unknown err:{e}')

    finally:

    time.sleep(2)

    if __name__ == '__main__':

    run()

    总结

    从上面看出,服务端主动通信给客户端,还是逃不过grpc的请求响应方式。上面代码只是实现了一种互相通信的方法,但是没有既充当客户端又充当服务端那种方法简单。

    展开全文
  • 智能卡数据传输概念

    2020-11-14 18:59:58
    因为,只用一条线来轮流发送和接收数据,卡和终端必须轮流传送或接收 数据,依次扮演收/发信人角色,这种通信方式称为半双工法。  全双工法中,双方都可以同时发送与接收数据(尚未在智能卡中实现)。然而,...
  • 串行通讯一条信息各位数据被逐位按顺序传送通讯方式称为串行通讯。串行通讯特点是:数据位传送,传按位顺序进行,最少只需一根传输线即可完成,成本低但送速度慢。串行通讯距离可以从几米到几千米。 根据...

    串行通讯

    一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。串行通讯的特点是:数据位传送,传按位顺序进行,最少只需一根传输线即可完成,成本低但送速度慢。串行通讯的距离可以从几米到几千米。 根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。信息只能单向传送为单工;信息能双向传送但不能同时双向传送称为半双工;信息能够同时双向传送则称为全双工。 串行通讯又分为异步通讯和同步通讯两种方式。在单片机中,主要使用异步通讯方式。

    串行通讯中,两个设备之间通过一对信号线进行通讯,其中一根为信号线,另外一根为信号地线,信号电流通过信号线到达目标设备,再经过信号地线返回,构成一个信号回路。

    初级读者会产生疑问:为何不让信号电流从电源地线返回?答案:公共地线上存在各种杂乱的电流,可以轻而易举地把信号淹没。因此所有的信号线都使用信号地线而不是电源地线,以避免干扰。

    这一对信号线每次只传送1bit(比特)的信号,比如1Byte(字节)的信号需要8次才能发完。传输的信号可以是数据、指令或者控制信号,这取决于采用的是何种通讯协议以及传输状态。串行信号本身也可以带有时钟信息,并且可以通过算法校正时钟。因此不需要额外的时钟信号进行控制。

    并行通讯中,基本原理与串行通讯没有区别。只不过使用了成倍的信号线路,从而一次可以传送更多bit的信号。

    并行通讯通常可以一次传送8bit、16bit、32bit甚至更高的位数,相应地就需要8根、16根、32根信号线,同时需要加入更多的信号地线。比如传统的PATA线路有40根线,其中有16根信号线和7根信号地线,其他为各种控制线,一次可以传送2Byte的数据。并行通讯中,数据信号中无法携带时钟信息,为了保证各对信号线上的信号时序一致,并行设备需要严格同步时钟信号,或者采用额外的时钟信号线。

    通过串行通讯与并行通讯的对比,可以看出:串行通讯很简单,但是相对速度低;并行通讯比较复杂,但是相对速度高。更重要的是,串行线路仅使用一对信号线,线路成本低并且抗干扰能力强,因此可以用在长距离通讯上;而并行线路使用多对信号线(还不包括额外的控制线路),线路成本高并且抗干扰能力差,因此对通讯距离有非常严格的限制。

    历史

    最早的计算机设备之间全部采用串行接口,比如硬盘接口、打印机接口、通讯端口等等。那时候都是分立元件的电路设计,如果采用并行接口,元件的数量和占用的空间将成倍增长。比如一个8bit并行线路的接口元件数量将是串行线路的8倍(你得为每根信号线配置一套接收电路)。这个时期的数据通讯只能是非常简单而低速的。

    但是集成电路技术的出现带来了一个转变,当大量元件可以集成到一个小小的芯片上时,并行通讯变得廉价而方便了。不论是8bit、16bit还是更高位数的并行线路,只需要一个并行接口芯片就可以处理,这比一个处理串行通讯的芯片成本高不到哪里去。与串行通讯相比,并行通讯在同样的工作频率下,通讯速度就可以整倍提高。因此适应了当时计算机设备发展的需要,硬盘、打印机等速度较快的设备开始使用并行通讯,PATA、SCSI、Parallel Port成为最为流行的并行通讯接口,被大众所熟知。不过并行线路固有的一些缺点仍然限制了并行通讯的应用范围,至于超高速通讯和长距离通讯方面,由于线路成本比接口成本要重要得多,因此一直都是串行通讯的应用领域。

    除了并行通讯具有速度优势以外,串行通讯自身也有一个问题。在计算机内部,数据往往都是并行方式传送的,当采用串行方式与外界通讯时必须经过串/并转换处理。在早期集成电路规模较小的时代,串/并转换电路的处理能力十分有限,因此串行通讯的速度无法提高。随着如今集成技术的发展,逻辑电路的集成能力大大提高,甚至超过了IO连接单元的集成水平,从而逐步解决了串/并转换速度的限制。另一方面,现在集成逻辑处理电路的成本也比IO连接单元更便宜,因此串行通讯再次显示出它的优势。如果说集成电路技术一度帮助并行通讯流行起来,那么现在的高度集成水平则帮助串行通讯重返主流应用领域。

    展开全文
  • 具有网络覆盖范围广、数据带宽宽、适应性强、计价按数据流量计算、实时在线优点,特别适用于间断、突发性或频繁、少量的数据传输,也适用于偶尔大量数据传输,完全满足数据采集及监控的双向数据信息传输。...
  • 串口线传输距离之讨论

    千次阅读 2015-06-25 11:04:39
    一条信息各位数据被逐位按顺序传送通讯方式称为串行通讯。串行通讯特点是:数据传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯距离可以从几米到...

    串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。串行通讯的特点是:数据位的传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯的距离可以从几米到几千米;根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。

    总述:

     

    串口通信的两种最基本的方式:同步串行通信方式和异步串行通信方式。

    同步串行是指SPI(Serial Peripheral interface)的缩写,顾名思义就是串行外围设备接口。SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息,TRM450是SPI接口。

    异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。UART是一个并行输入成为串行输出的芯片,通常集成在主板上。UART包含TTL电平的串口RS232电平的串口。 TTL电平是3.3V的,而RS232是负逻辑电平,它定义+5~+12V为低电平,而-12~-5V为高电平,MDS2710、MDS SD4、EL805等是RS232接口,EL806有TTL接口。

    串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

    RS-232:

     

    也称标准串口,最常用的一种串行通讯接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是“数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准”。传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座(DB25),后来使用简化为9芯D型插座(DB9),现在应用中25芯插头座已很少采用。

    RS-232采取不平衡传输方式,即所谓单端通讯。由于其发送电平与接收电平的差仅为2V至3V左右,所以其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。

    pt; background:rgb(255,255,255); mso-shading:rgb(255,255,255); " >、RS485等。RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

    RS-422

     

    标准全称是“平衡电压数字接口电路的电气特性”,它定义了接口电路的特性。典型的RS-422是四线接口。实际上还有一根信号地线,共5根线。其DB9连接器引脚定义。由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余为从设备(Slave),从设备之间不能通信,所以RS-422支持点对多的双向通信。接收器输入阻抗为4k,故发端最大负载能力是10×4k+100Ω(终接电阻)。RS-422四线接口由于采用单独的发送和接收通道,因此不必控制数据方向,各装置之间任何必须的信号交换均可以按软件方式(XON/XOFF握手)或硬件方式(一对单独的双绞线)实现。

    RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mb/s。

    RS-485

     

    是从RS-422基础上发展而来的,所以RS-485许多电气规定与RS-422相仿。如都采用平衡传输方式、都需要在传输线上接终接电阻等。RS-485可以采用二线与四线方式,二线制可实现真正的多点双向通信,而采用四线连接时,与RS-422一样只能实现点对多的通信,即只能有一个主(Master)设备,其余为从设备,但它比RS-422有改进,无论四线还是二线连接方式总线上可多接到32个设备。

    RS-485与RS-422的不同还在于其共模输出电压是不同的,RS-485是-7V至+12V之间,而RS-422在-7V至+7V之间,RS-485接收器最小输入阻抗为12kΩ、RS-422是4kΩ;由于RS-485满足所有RS-422的规范,所以RS-485的驱动器可以在RS-422网络中应用。

    RS-485与RS-422一样,其最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能使用规定最长的电缆长度。只有在很短的距离下才能获得最高速率传输。一般100米长双绞线最大传输速率仅为1Mb/s。

    nt face="Arial" >1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mb/s。

    USB

     

    USB接口是电脑主板上的一种四针接口,其中中间两个针传输数据,两边两个针给外设供电。USB接口速度快、连接简单、不需要外接电源,传输速度12Mbps,最新USB2.0可达480Mbps;电缆最大长度5米,USB电缆有4条线,2条信号线,2条电源线,可提供5伏特电源,USB电缆还分屏蔽和非屏蔽两种,屏蔽电缆传输速度可达12Mbps,价格较贵,非屏蔽电缆速度为1.5Mbps,但价格便宜;USB通过串联方式最多可串接127个设备;支持热插拔。

    与并口区别

     

    串口形容一下就是一条车道,而并口就是有8个车道同一时刻能传送8位(一个字节)数据。这样数据传输速度大大提高,但是并不是说并口快,由于8位通道之间的互相干扰(串扰),传输时速度就受到了限制,传输容易出错。串口没有互相干扰。并口同时发送的数据量大,但要比串口慢。串口硬盘就是这样被人们重视的。

     

     

     

     

     

     

     

    展开全文
  • Socket 也称为“套接字”,是网络通信中概念,它分为流式套接字和用户数据报套接字两种,分别对应网络的传输控制层中 TCP 和 UDP 协议,TCP 协议是面向连接协议,提供稳定的双向功能,TCP 链接建立经过“三...

    Socket 也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应网络的传输控制层中的 TCP 和 UDP 协议,TCP 协议是面向连接的协议,提供稳定的双向功能,TCP 链接的建立经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有很高的稳定性;而 UDP 是无连接的,提供不稳定的单向连接功能,当然 UDP 也可以实现双向通信功能。在性能上,UDP 具有更好的效率,其缺点是不能保证数据一定能正确传输。尤其是在网络拥堵的情况下。接下来我们演示一个跨进程聊天程序,两个进程可以通过 Socket 来实现信息的传输,Socket 本身可以支持传输任意字节流的。

    使用 Socket 来进行通信,有两点需要注意,首先需要声明权限:

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

    其次要注意不鞥在主线程中访问网络。这样会导致无法再 Android 4.0 及其以上的设备中运行,会抛出如下异常:android.os.NetwworkOnMainThreadException。下面开始我们的聊天程序,比较简单,首先在远程 Socket 建立一个 TCP 服务,然后在主界面中链接 TCP 服务,连接上了以后,就可以给服务端发消息。对于我们发送的每一条文本信息,服务端都会随机的回应我们一句话。为了更好的展示 Socket 的工作机制,在服务端我们需要处理下,使其能够和多个客户端同时建立链接并相应。

    先看一下服务端设计,当 Service 启动时,会在线程中建立 TCP 服务,这里监听的是 8688 端口,然后就可以等待客户端的连接请求。当客户端连接时,就会生成一个新的 Socket,通过每次新创建的 Socket 就可以分别和不同的客户端通信了。服务端每收到一次客户端的信息就会随机回复一句话给客户端。当客户端连接断开时,服务端也会相应的关闭对应的 Socket 并结束通话线程,这点如何做到呢?方法有很多,这里是通过判断服务端输入的流的返回值来确定的,当客户端断开连接后,服务端这边的输入流会返回 null,这个时候就知道客户端退处理,服务端代码如下:

    public class TCPServerService extends Service {
    
        public static final String TAG = "TCPServerService";
        private boolean mIsServiceDestoryed = true;
        private String [] mDefiendMessages = new String[]{"你好啊,哈哈", "请问你叫什么名字啊?", "今天北京天气不错啊,shy", "你知道吗,我可是可以和多个人同事聊天的哦", "给你讲个笑话吧"};
    
        @Override
        public void onCreate() {
            super.onCreate();
            new Thread(new TCPServer()).start();
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            mIsServiceDestoryed = false;
        }
    
        private class TCPServer implements Runnable {
    
            @Override
            public void run() {
                ServerSocket serverSocket = null;
                try {
                    //监听 8688 接口
                    serverSocket = new ServerSocket(8688);
                } catch (IOException e) {
                    System.err.println("establish tcp server filled, port: 8688");
                    e.printStackTrace();
                    return;
                }
    
                while (mIsServiceDestoryed) {
                    try {
                        //接受客户端请求
                        final Socket client = serverSocket.accept();
                        new Thread() {
                            @Override
                            public void run() {
                                try {
                                    responseClient(client);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }.start();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            private void responseClient(Socket client) throws IOException {
                //用于接收客户端消息
                BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                //用于向客户端发送消息
                PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
                out.println("欢迎来到聊天室!");
                while (mIsServiceDestoryed){
                    String str = in.readLine();
                    Log.i(TAG, "msg from client:" + str);
                    if(str == null) {
                        //客户端断开连接
                        break;
                    }
                    int i = new Random().nextInt(mDefiendMessages.length);
                    String msg = mDefiendMessages[i];
                    out.println(msg);
                    Log.i(TAG, "send msg:" + msg);
                }
                Log.i(TAG, "client quit.");
                close(out);
                close(in);
                client.close();
            }
        }
    
        public void close(Closeable closeable) {
            try {
                if (closeable != null) {
                    closeable.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }

    接着看一下客户端,客户端 Activity 启动时,会在 onCreate 中开启一个线程去链接服务端的 Socket,至于为什么用线程在前边已经做了介绍。为了确定能够连接成功,这里采用了超时重连的策略,每次连接失败后都会重新尝试建立连接,当然为了降低重试机制的开销,我们加入了休眠机制,即每次重试的时间间隔是 1000 毫秒。

            Socket socket = null;
            while (socket == null) {
                try {
                    socket = new Socket("localhost", 8688);
                    mClientSocket = socket;
                    mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
                    mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
                    Log.i(TAG, "connect server success.");
                } catch (IOException e) {
                    e.printStackTrace();
                    SystemClock.sleep(1000);
                    Log.i(TAG, "connect tcp server failed, retry...");
                }
            }

    服务端连接成功后,就可以和服务端进行通信了。下面的代码在线程中通过 while 循环不断地从服务端获取数据,同时在 Activity 退出时,就退出循环并终止线程。

      //接收服务端消息
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while (!SocketActivity.this.isFinishing()){
                    String msg = br.readLine();
                    Log.e(TAG, "receive msg:" + msg);
                    if(null != msg) {
                        String time = new SimpleDateFormat("HH:mm:ss").format(new Date(System.currentTimeMillis()));
                        final String showMsg = "server" + time + ":" + msg + "\n";
                        mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showMsg).sendToTarget();
                    }
                }
                close(mPrintWriter);
                close(br);
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

    同时,在 Activity 退出时,还要关闭当前的 Socket。如下所示:

        @Override
        protected void onDestroy() {
            super.onDestroy();
            if(mClientSocket != null) {
                try {
                    mClientSocket.shutdownInput();
                    mClientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    接着和发送消息的过程,这个就简单了,这里不再详细说明,客户端完整的代码如下:

    public class SocketActivity extends Activity implements View.OnClickListener {
    
        private static final String TAG = "SocketActivity";
    
        private Socket mClientSocket;
        private PrintWriter mPrintWriter;
        private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
        private static final int MESSAGE_SOCKET_CONNECTED = 2;
    
        private Button mSendButton;
        private TextView mMessageTextView;
        private EditText mMessageEditText;
    
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MESSAGE_RECEIVE_NEW_MSG:
                        mMessageTextView.setText(mMessageTextView.getText().toString() + msg.obj);
                        break;
                    case MESSAGE_SOCKET_CONNECTED:
                        mSendButton.setEnabled(true);
                        break;
                    default:
                        break;
                }
            }
        };
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_socket);
            mMessageTextView = (TextView) findViewById(R.id.msg_container);
            mSendButton = (Button) findViewById(R.id.send);
            mSendButton.setOnClickListener(this);
            mMessageEditText = (EditText) findViewById(R.id.msg);
    
            startService(new Intent(this, TCPServerService.class));
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    connectTCPServer();
                }
            }).start();
    
        }
    
        @Override
        public void onClick(View v) {
            final String msg = mMessageEditText.getText().toString();
            if(!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                mPrintWriter.println(msg);
                mMessageEditText.setText("");
                String time = new SimpleDateFormat("HH:mm:ss").format(new Date(System.currentTimeMillis()));
                final String showMsg = "self" + time + ":" + msg + "\n";
                mMessageTextView.setText(mMessageTextView.getText().toString() + showMsg);
            }
        }
    
        private void connectTCPServer() {
            Socket socket = null;
            while (socket == null) {
                try {
                    socket = new Socket("localhost", 8688);
                    mClientSocket = socket;
                    mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
                    mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
                    Log.i(TAG, "connect server success.");
                } catch (IOException e) {
                    e.printStackTrace();
                    SystemClock.sleep(1000);
                    Log.i(TAG, "connect tcp server failed, retry...");
                }
            }
    
            //接收服务端消息
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while (!SocketActivity.this.isFinishing()){
                    String msg = br.readLine();
                    Log.e(TAG, "receive msg:" + msg);
                    if(null != msg) {
                        String time = new SimpleDateFormat("HH:mm:ss").format(new Date(System.currentTimeMillis()));
                        final String showMsg = "server" + time + ":" + msg + "\n";
                        mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showMsg).sendToTarget();
                    }
                }
                close(mPrintWriter);
                close(br);
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public void close(Closeable closeable) {
            try {
                if (closeable != null) {
                    closeable.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if(mClientSocket != null) {
                try {
                    mClientSocket.shutdownInput();
                    mClientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    上述就是通过 Socket 来进行进程间通信的实例,除了采用 TCP 套接字,还可以采用 UDP 套接字,另外,上面的例子仅仅是一个示例,实际上通过 Socket 不仅能实现进程间他通信,还可以实现设备间的通信,当然前提是这些设备之间的 IP 地址互相可见,这里就不一一说明了。下面我们看下运行效果:

     

    展开全文
  • 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。 建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络...
  • 基本概念 TCP协议:Transmission Control Protocol 传输控制协议TCP是一种面向...Socket:网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。 建立网络通信连接至少要一对...
  • 根据信息在传输线上传送方向,分为以下三种通信方式: 单工通信:单向传输 半双工通信:双相交替传输 全双工通信:双向同时传输。 带通调制 用调制信号去调制一个载波,使载波某个(些)参数随基带信号...
  • 摘要:数据线、红外线、蓝牙、Wi-Fi、二维码、NFC这些都是手机曾经使用或者正在使用的与其它电子设备之间进行数据传输的方式,它们可以实现手机与手机之间、手机与电脑之间或者手机与网络之间进行数据传输。...
  • 摘要:数据线、红外线、蓝牙、Wi-Fi、二维码、NFC这些都是手机曾经使用或者正在使用的与其它电子设备之间进行数据传输的方式,它们可以实现手机与手机之间、手机与电脑之间或者手机与网络之间进行数据传输。...
  • 0. 串口通讯数据传输方式:单工(单向传输数据),半双工(非同时双向传输),全双工(同时,双向传输) 1. 根据通信方式的不同又分为同步通讯和异步通讯。 同步通讯:所有设备都使用同一个时钟,称为同步时钟。...
  • 信息可以同时双向传输,需要由两条物理上独立传输线路或者需要一条物理线路上两个信道分别用于不同方向信号传输 波特率 又称为码元速率、调制速率、信号传输速率,单位是baud Sbit=Bbaud*log
  • 一条信息各位数据被逐位按顺序传送通讯方式称为串行通讯。串行通讯特点是:数据位传送,传按位顺序进行,最少只需一根传输线即可完成,成本低但送速度慢。串行通讯距离可以从几米到几千米。 根据信息传送...
  • 170806 通信-ZigBee协议栈串口实验

    千次阅读 2017-08-06 11:17:18
    1625-5 王子昂 总结《2017年8月5日》 【连续第307天总结】 A. ZigBee协议栈串口通信 B. 串行简介 串行接口(Serial Interface) 是指数据一位一位...一条信息各位数据被逐位按顺序传送通讯方式称为串行通讯。串行
  • 单片机串口通信 ...一条信息各位数据被逐位按顺序传送通讯方式称为串行通讯。串行通讯特点是:数据传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯...
  • 串口与并口区别

    万次阅读 2017-02-23 22:07:16
     ...一条信息各位数据被逐位按顺序传送通讯方式称为串行通讯。串行通讯特点是:数据位传送,传按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯距离可以从几米到
  • 文章目录基本概念传输媒介引导型传输媒体非导引型传输媒体传输方式串行和并行同步传输和异步传输单工、半双工、全双工单向通信(单工)双向交替通信(半双工)双向同时通信(全双工)编码与调制 基本概念 在物理层上传送...
  • Mística具有模块化设计,围绕一种称为SOTP:简单覆盖传输协议自定义传输协议构建。数据被加密,分块并放入SOTP数据包中。 SOTP数据包经过编码并嵌入到应用程序协议所需字段中,然后发送到另一端。 SOTP层...
  • 每日练习109

    2018-10-09 14:37:14
    1.在同一信道上同一时刻,可进行双向数据传送通信方式是()。 C 单工 半双工 全双工 上述三种均不是 如果在通信过程任意时刻,信息只能由一方A传到另一方B,则称为单工。 如果在任意时刻,信息既可由A传到B,又...
  • 计算机网络---物理层

    2019-09-30 01:00:07
    基带信号:将数字信号1和0直接用两种不同电压表示,然后送到数字信道上传输称为基带传输) 宽带信号:将基带信号进行调制后形成频分复用模拟信号,然后传送到模拟信道上取传输称为宽带传输) 从通信双方信息...
  • 邮槽(Mailslot)也称为邮件槽,它是 Windows 提供一种用来实现进程间通信手段,是单向数据传输的服务。 邮槽弊端:邮槽是单向通信。服务器端只能读取,客户端只能写入。如果要双向通信就需要服务端写一个邮槽...
  • 随着三金工程尤其是金卡工程的启动,DES算法在POS、ATM、磁卡及智能卡(IC卡)、加油站、高速公路收费站等领域被广泛应用,以此来实现关键数据的保密,如信用卡持卡人的PIN的加密传输,IC卡与POS间的双向认证、金融...
  • 串行通讯

    2021-02-03 17:55:33
     一条信息各位数据被同时传送通讯方式称为并行通讯。并行通讯特点是:各数据位同时传送,传送速度快、效率高,但有多少数据位就需多少根数据线,因此传送成本高,且只适用于近距离(相距数米)通讯。 一条...
  • 但听到有人说,他使用10BASE-T以太网传送数据的距离达到180 m。这可能吗? 问题4-14:粗缆以太网有一个单独的收发器。细缆以太网和双绞线以太网有没有收发器?如果有,都在什么地方? 问题4-15:什么叫做“星形总线...

空空如也

空空如也

1 2 3 4 5
收藏数 89
精华内容 35
关键字:

双向传输数据的方式称为