精华内容
下载资源
问答
  • 套接字为我们客户和服务器通信提供了这样一种机制,就像插头一样,把两个套接字连载一起,套接字意思是插座,通信就是一个线把两头连载一起,服务器一定是要运行起来的,通常来说在一个主机上有多个进程,而每一个...
    284560fde00d828a2d24d690b8ac4c87.png
    c49f0cf982719e5206cce13c584943ef.png
    573e9480825d762f53a0466b4070069f.png

    套接字为我们客户和服务器通信提供了这样一种机制,就像插头一样,把两个套接字连载一起,套接字意思是插座,通信就是一个线把两头连载一起,服务器一定是要运行起来的,通常来说在一个主机上有多个进程,而每一个服务器应用程序,为了支持客户端进程能够与他进行通信,按照套接字的机制必须创建套接字,一个进程可能创建多个套接字,不同进程创建自己的套接字,当某一个客户端套接字想要与某一个服务进行通信的时候,那么问题就来了,我们怎么样明确,我们与哪个进程通信,事实转变为 我要插到哪个套接字,一个IP地址可以唯一的标识一个主机,一个主机上由于有多个应用进程在运行,每个应用进程可能使用多个套接字,靠ip没法标识与哪个套接字进行通信。

    标识通信端点(对外)

    IP地址+端口号

    操作系统/进程如何管理套接字(对内):

    套接字描述符 socket descriptor

    298055786fd4b9f58feb8ada95cd75e6.png

    套接字结构

    bcc39efbdde3f0d30de5491d6719bfd1.png
    ba413f6bb659bebce8e663d95233182b.png
    fc586fdf27b6737e9f5c01dfd1ecefb4.png
    270adefc70603caae425c832ba438dd9.png
    72f42da6f5f99e7bb33e735cd28466fb.png
    d6c9d38c843638dd728bd05e15bad81c.png
    bd3671576a236205788d0fc5b6c0bb85.png
    72af3c43be8c9d70d1bb8f533b49b581.png
    e7c16fda932e87a1eb53b5731f365faa.png

    重点,accept

    4d708dc27ed8d0740316ac203b3bb845.png

    解释为什么要用新创建的套接字与客户端通信

    tcp是一个可靠的面向字节流的传输协议并且还有一个特点,是点对点的,点对点的意味着一个tcp连接只能连接客户端和服务端这俩套接字,假如我们的tcp服务器不这样做,当一个客户链接请求来了以后,就用主套接字与客户端进行tcp连接通信,tcp服务器在某一时刻只能与一个客户端进行通信,就不可能实现并发的tcp服务器。为了实现并发tcp服务器,accept取到一个连接请求,不用主套接字为你提供服务,创建一个新的套接字与客户端通信,如果我们的服务器通过多线程多进程实现并发服务器,主线程可以继续接受新的客户连接请求,创建一个新的socket提供服务。

    88109b1c7eac343d41f4046003ac252d.png
    ca12884c48d4c3da117b4c3c41ac1046.png
    670907e51d5accc43a5c55391d563b3f.png
    c6f726fe2baac363161f69b9488dcb66.png
    cd156232da9a79ec4bfb19bf60ebea3e.png
    dd6568df56deb778f92eeb1b251837f2.png
    02b5e6d52221e4bfc5951f02ee568ef9.png
    0aedd862ad45cbc033be2093ec04e348.png
    ab8ebf550033211cb58ec981fd687a96.png
    展开全文
  • 背景图来源:click本篇介绍Socket理论知识,适用于大部分同学,帮助理解,其中会涉及到Android,Python,Java在Android中,我们都知道进程间通信方式有下面几种方式(1)Bundle (对应于四大组件)/Activity,Service...

    a75045412e7793785f18ea1a9cb2b011.png

    背景图来源:click

    本篇介绍Socket理论知识,适用于大部分同学,帮助理解,其中会涉及到Android,Python,Java

    在Android中,我们都知道进程间通信方式有下面几种方式

    • (1)Bundle (对应于四大组件)/Activity,Service,Broadcast Receiver,Content Provider
    • (2)文件共享/也就是序列化
    • (3)ContentProvider(基于Binder)
    • (4)AIDL Service(基于Binder)
    • (5)Messenger(基于Binder)
    • (6)Socket(网络)

    这些IPC的方式都有所差异,有的属于应用间进程通信,比如Android四大组件,有的是基于Binder实现的,广播也是一种被动跨进程通讯的方式,在Android开发中,Binder是一种进程间通信机制,Linux是进程隔离的操作系统,Android集成了Linux,但Binder却是Android特有的进程通信方式。本篇内容先介绍其中最好玩的:Socket(网络)跨进程通信理论1。

    从这篇文章你能了解到如何网络编程开发一个真正跨进程通信的应用软件,下一篇文章则会重点完成【跨进程通信之Socket-项目实践2】

    在使用Socket实现进程间通信前,先对计算机网络,网络编程,协议等相关知识进行简单回顾

    一:计算机网络

    虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多。计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM、Apple和Microsoft都有各自的网络协议,互不兼容,这就好比一群人有的说英语,有的说中文,有的说德语,说同一种语言的人可以交流,不同的语言之间就不行了。

    为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为了实现互联网这个目标,互联网协议簇(Internet Protocol Suite)就是通用协议标准。Internet是由inter和net两个单词组合起来的,原意就是连接“网络”的网络,有了Internet,任何私有网络,只要支持这个协议,就可以联入互联网

    而计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信,在计算机网络编程中设计到网络分层,以及TCP/IP协议,而这些是进行[跨进程通信之Socket-理论1]的基础,下面分别对这些点进行简单的讲解

    网络编程

    网络编程就是如何在程序中实现两台计算机的通信

    举个例子,当你使用浏览器访问新浪网时,你的计算机就和新浪的某台服务器通过互联网连接起来了,然后,新浪的服务器把网页内容作为数据通过互联网传输到你的电脑上

    由于你的设备(如电脑)上可能不止浏览器,还有QQ、Skype、WhatsApp、邮件客户端等,不同的程序连接的别的计算机也会不同,所以,更确切地说,网络通信是两台计算机上的两个进程之间的通信

    网络通信是两台计算机上的两个进程之间的通信。

    网络编程对所有开发语言都是一样的,Python,Java,C。用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信。

    网络分层

    从大学计算机网络基础中我们知道,一般情况会将网络分为5层:

    • 应用层 常见协议:HTTP、FTP、POP3等
    • 传输层 常见协议:TCP、UDP
    • 网络层 常见协议:IP
    • 数据链路层
    • 物理层

    7e86b6607e7abae47d094d57fc7a4300.png
    计算机网络拓扑图

    TCP/IP简介

    计算机网络中,互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。

    通信的时候,双方必须知道对方的标识,好比发邮件必须知道对方的邮件地址。互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123。如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。

    IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。

    那网络数据到底怎么传递过去的?

    解释清楚了,这里有个思考,两个人沟通是用声音传递的,这个就是介质,假如我跟小团子两个人在一起吃饭,我们之间的聊天都是通过声音传递的,你不能问我,为什么我说话就她就能听到吧,声音就是介质,网络中也是一样ip包就是介质,传递的方式就是信号

    这里再思考,风吹过,树叶沙沙作响,风从开始的位置到指定的位置,这个就是能通信的距离,时间呢就是传递这么远需要的时间,如果不是风,是光呢?如果两个物体之间以光速传递,效率是非常快的

    IP地址实际上是一个32位整数(称为IPv4),以字符串表示的IP地址如192.168.0.1实际上是把32位整数按8位分组后的数字表示,目的是便于阅读。
    IPv6地址实际上是一个128位整数,它是目前使用的IPv4的升级版,以字符串表示类似于2001:0db8:85a3:0042:1000:8a2e:0370:7334。

    TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。

    TCP、UDP

    tcp,udp是在ip之上的协议

    TCP:面向连接的、可靠的流协议,提供可靠的通信传输。

    所谓流,就是指不间断的数据结构,你可以把它想象成排水管道中的水流。当应用程序采用TCP发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。

    UDP:面向无连接的,具有不可靠性的数据报协议。(让广播和细节控制交给应用的通信传输)

    简单总结

    TCP用于在传输层有必要实现可靠传输的情况,由于它是面向连接并具有“顺序控制”、丢包重发控制等机制;而UDP无顺序控制、无丢包重发机制,主要用于那些对高速传输和实时性有较高要求的通信或广播通信。

    因此TCP和UDP应该根据应用的目的按需使用,没有绝对优缺点。

    TCP三次握手/四次挥手

    使用TCP协议的连接建立与断开,正常过程下至少需要发送7个包才能完成,就是我们常说的三次握手,四次挥手。

    标志位Flags、序号

    1. 序列号 Sequeuece number(seq): 数据包本身的序列号,初始序列号是随机的。
    2. 确认号 Acknowledgment number(ack): 在接收端,用来通知发送端数据成功接收
    3. 标志位,标志位只有为 1 的时候才有效
    SYN(synchronize):表示在连接建立时用来同步序号。ACK:TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1.FIN(finish):用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。

    三次握手

    指建立一个TCP连接时需要客户端和服务端总共发送3个包确认连接的建立。在Socket编程中,这一个过程由客户端执行connect来触发。

    cd37154ada38da55490d566232a26c50.png
    TCP三次握手
    1. 第1次握手:客户端向服务端发送请求报文;即SYN=1,ACK=0,seq=x。
    2. 第2次握手:服务端收到客户端的请求报文,服务端会确认应答,告诉客户端已经收到请求了;即SYN=1,ACK=1,seq=y,ack=x+1;
    3. 第3次握手:客户端收到服务端的确认应答后,再次向服务端进行确认应答,建立完整的连接;即ACK=1,seq=x+1,ack=y+1

    四次挥手

    终止TCP连接,就是断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在Socket编程中,这一工程由客户端或服务端任意一方执行close来触发。

    由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这一原则是当一方数据发送完成,发送一个标志位为 FIN 的报文来终止这一方向的连接,收到标志位为 FIN 的报文意味着这一方向上不会再收到数据了。但是在 TCP 连接上仍然能够发送数据,直到这一方向也发送了 FIN 。发送 FIN 的一方执行主动关闭,另一方则执行被动关闭

    60869a2fd00f7e6e306ecbfaf384818e.png
    TCP连接释放过程
    1. 第1次挥手:客户端发送一个FIN=1,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说”我客户端没有数据要发给你了”,但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
    2. 第2次挥手:服务器端收到FIN后,先发送ack=u+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。
    3. 第3次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=1报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
    4. 第4次挥手:客户端收到FIN=1报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=w+1后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。
    许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。

    一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。

    端口有什么作用呢?

    在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个TCP报文来了之后,到底是交给浏览器还是Instagram,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。

    一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口

    二:Socket通信

    平台: Java,Python,Android

    Socket通信简介

    一般来说客户端与服务器的通信方式主要有两种,一是Http通信,一是Socket通信。两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据。而Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求。在程序内部提供了与外界通信的端口,通过建立socket连接,可为通信双方的数据传输传提供通道。

    socket的主要特点有数据丢失率低,使用简单且易于移植。

    Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可

    通过网络拓扑7层模型可以看到

    • HTTP是位于应用层
    • Socket在传输层
    • RPC(Remote Procedure Call)远程过程调用位于会话层

    Socket的分类

    socket是基于TCP或UDP协议实现的,下面看一下Socket 基本通信模型

    Socket 基本通信模型

    4e397e50dcb5f8cf42d7694dff675802.png
    Socket 基本通信模型

    TCP通信模型

    be778ab989c2c2e7c0c022b7ea3bcaa2.png
    TCP通信模型

    UDP通信模型

    8673915b26895614663d9982d92b5e9a.png
    UDP通信模型

    Socket分类

    根据不同的的底层协议,Socket的实现是多样化的。这里只介绍TCP/IP协议族的内容,在这个协议族当中主要的Socket类型为流套接字(streamsocket)和数据报套接字(datagramsocket)。流套接字将TCP作为其端对端协议,提供了一个可信赖的字节流服务。数据报套接字使用UDP协议,提供数据打包发送服务

    socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供了网络开发所用的接口;HTTP是轿车,提供了封装或显示数据的具体形式;socket是发动机,提供了网络通信的能力

    三:Socket简单实现

    Socket典型的应用就是C/S结构,由Sokcet通信模型可知,socket的使用是基于TCP或UDP协议

    虽然本篇文章题目为[理论1],但我们还是要结合代码来简单使用一下Socket,便于下一篇文章[项目实践2]更好的完成

    下面我将同时使用python和java语言来完成Socket跨进程通信-简单数据传输

    基于TCP协议的Socket

    客户端

    大多数连接都是可靠的TCP连接。创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器

    所以,我们要创建一个基于TCP连接的Socket,

    ###Java里面可以这样写

    Socket mSocket = new Socket("新浪首页", 80);
    //connect(new InetSocketAddress("新浪首页",80));
    //判断是否连接成功
    if (mSocket.isConnected()){
    	mHandler.sendEmptyMessage(CONNECT_SERVER_SUCCESS);
    }else{
    	mHandler.sendEmptyMessage(CONNECT_SERVER_FAIL);
    }
    连接成功以后就是读取数据可以参考
    while (true)
    {
    	byte[] buf = new byte[10000];
    	int size = 0;
    	if ((size = in.read(buf)) != -1)
    	{
    		readByte = new String(buf, 0, size, "GBK");
    		if (!TextUtils.isEmpty(readByte))
    		{
    			Log.d("--qydq-->readByte--->", readByte);
    			//发消息通知更新UI
    			mHandler.obtainMessage(MESSAGE_RECEIVE_SUCCESS, readByte).sendToTarget();
    		}
    	}
    }

    ###Python里面的写法:

    # 导入socket库:
    import socket
    # 创建一个socket:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 建立连接:
    s.connect(('新浪首页', 80))

    创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议,这样,一个Socket对象就创建成功,但是还没有建立连接。

    客户端要主动发起TCP连接,必须知道服务器的IP地址和端口号。新浪网站的IP地址可以用域名http://www.sina.com.cn自动转换到IP地址,但是怎么知道新浪服务器的端口号呢?

    答案是作为服务器,提供什么样的服务,端口号就必须固定下来。由于我们想要访问网页,因此新浪提供网页服务的服务器必须把端口号固定在80端口,因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号,例如SMTP服务是25端口,FTP服务是21端口,等等。端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。

    因此,我们连接新浪服务器的代码如下:

    s.connect(('新浪首页', 80))

    Tips:

    connect参数是一个tuple,包含地址和端口号

    建立TCP连接后,我们就可以向新浪服务器发送请求,要求返回首页的内容:

    # 发送数据:
    s.send(b'GET / HTTP/1.1rnHost: 新浪首页rnConnection: closernrn')

    TCP连接创建的是双向通道,双方都可以同时给对方发数据。但是谁先发谁后发,怎么协调,要根据具体的协议来决定。例如,HTTP协议规定客户端必须先发请求给服务器,服务器收到后才发数据给客户端

    4e4ffcefdc880e913e7aab0e3729fe28.png
    连接baidu.com端口占用异常

    如果出现这个错误,则是因为端口被占用了,或者远程服务器拒绝,使用下面方法kill掉占用的端口即可

    netstat -ano|findstr 80
    tasklist |findstr 9609找出对应pid的进程详细信息
    taskkill /pid 9609 /F杀掉该进程

    TCP建立连接,双向通道打开以后,然后就是发数据了,发送的文本格式必须符合HTTP标准,如果格式没问题,就可以接收到新浪服务器(网络限制接不了新浪,用百度,原理是一样的)返回的数据了:

    # 接收数据:
    buffer = []
    while True:
        # 每次最多接收1k字节:
        d = s.recv(1024)
        if d:
            buffer.append(d)
        else:
            break
    data = b''.join(buffer)

    如下接受到的数据结果

    b'HTTP/1.1 301 Nored PermanentlyrnServer:PAAS-WEBrnData:Toda 2019 THE TIME GMTrnContent-Type:text/htmlrnContent-Length:.....

    接收数据时,调用recv(max)方法,一次最多接收指定的字节数,因此,像Java处理数据一样在一个while循环中反复接收,直到recv()返回空数据,表示接收完毕,退出循环

    当我们接收完数据后,调用close()方法关闭Socket,这样,一次完整的网络通信就结束了:

    # 关闭连接:
    s.close()

    接收到的数据包括HTTP头和网页本身,我们只需要把HTTP头和网页分离一下,把HTTP头打印出来,网页内容保存到文件:

    header, html = data.split(b'rnrn', 1)
    print(header.decode('utf-8'))
    # 把接收的数据写入文件:
    with open('baidu.html', 'wb') as f:
        f.write(html)

    现在,只需要在浏览器中打开这个baidu.html文件,就可以看到百度的首页了。

    服务器

    和客户端编程相比,服务器编程就要复杂一些。

    服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。

    所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。

    但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。

    ###Java里面:

    1、创建ServerSocket对象,指定与客户端一样的端口号

    ServerSocket serverSocket = new ServerSocket(9609);

    当然也可以显式指定服务器要绑定的IP地址,该构造方法适用于具有多个IP地址的主机。

    serverSocket.bind();

    2、获取Socket实例

    从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的Socket对象,并将它返回

    serverSocket.accept();

    3、获取输入流,接受客户端发来的消息

    InputStream inputStream = socket.getInputStream();

    4、获取输出流,向客户端发送消息回应

    OutputStream outputStream = socket.getOutputStream();

    5、关闭IO资源

    close();

    完整Java服务器端写法参考:

    ServerSocket serverSocket = new ServerSocket(9609);
    final Socket socket = serverSocket.accept();
    // socket.connect();
    while (true){
    	new Thread(new Runnable()
    	{
    		@Override
    		public void run()
    		{
    			try
    			{
    				socketTask(socket);
    			}
    			catch (IOException e)
    			{
    				e.printStackTrace();
    			}
    		}
    	}).start();
    }
    private static void socketTask(final Socket socket) throws IOException
    {
    	InputStream inputStream = socket.getInputStream();
    	InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
    	BufferedReader reader = new BufferedReader(inputStreamReader);
    	String info = reader.readLine();
    	System.out.println("服务端收到客户端的信息: " + info);
    
    	OutputStream outputStream = socket.getOutputStream();
    	PrintWriter writer = new PrintWriter(outputStream);
    	writer.write("成都欢迎你!" + "n");
    	writer.flush();
    
    	inputStream.close();
    	reader.close();
    	outputStream.close();
    	writer.close();
    }

    ###Python里面

    我们来编写一个简单的服务器程序,它接收客户端连接,把客户端发过来的字符串加上【Hello】再发回去

    首先,创建一个基于IPv4和TCP协议的Socket:

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    然后,我们要绑定监听的地址和端口。服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用0.0.0.0绑定到所有的网络地址,还可以用127.0.0.1绑定到本机地址。

    127.0.0.1是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。

    端口号需要预先指定。因为我们写的这个服务不是标准服务,所以用9609(0609生日)这个端口号。

    Tips:

    小于1024的端口号必须要有管理员权限才能绑定。

    # 监听端口:

    s.bind(('127.0.0.1', 9609))

    紧接着,调用listen()方法开始监听端口,传入的参数指定等待连接的最大数量:

    s.listen(5)
    print('Waiting for connection...')

    接下来,服务器程序通过一个永久循环来接受来自客户端的连接,accept()会等待并返回一个客户端的连接:

    while True:
        # 接受一个新连接:
        sock, addr = s.accept()
        # 创建新线程来处理TCP连接:
        t = threading.Thread(target=tcplink, args=(sock, addr))
        t.start()

    每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接:

    def tcplink(sock, addr):
        print('Accept new connection from %s:%s...' % addr)
        sock.send(b'Welcome!')
        while True:
            data = sock.recv(1024)
            time.sleep(1)
            if not data or data.decode('utf-8') == 'exit':
                break
            sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
        sock.close()
        print('Connection from %s:%s closed.' % addr)

    连接建立后,服务器首先发一条欢迎消息,然后等待客户端数据,并加上Hello再发送给客户端。如果客户端发送了exit字符串,就直接关闭连接。

    要测试这个服务器程序,我们还需要编写一个客户端程序:

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 建立连接:
    s.connect(('127.0.0.1', 9609))
    # 接收欢迎消息:
    print(s.recv(1024).decode('utf-8'))
    for data in [b'Scofield', b'sunst', b'lifangfang']:
        # 发送数据:
        s.send(data)
        print(s.recv(1024).decode('utf-8'))
    s.send(b'exit')
    s.close()

    我们需要打开两个命令行窗口,一个运行服务器程序,另一个运行客户端程序,就可以看到效果了

    Tips:

    客户端程序运行完毕就退出了,而服务器程序会永远运行下去,必须按Ctrl+C退出程序

    基于UDP协议的数据传输

    TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据。相对TCP,UDP则是面向无连接的协议。

    使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。

    虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。

    服务器端

    首先Step1:创建一个DatagramSocket对象,并且指点监听的端口。接下来创建一个空的DatagramSocket对象用于接收数据

    socket = new DatagramSocket(9609); 
    byte data[]=new byte[4*1024]
    DatagramSocket packet=new DatagramSocket(data,data.length)

    Step2:使用DatagramSocket的receive方法接收客户端发送的数据

    //读取接收到得数据    
    socket.receive(packet); 

    ###Java服务器端参考

    public void ServerReceviedByUdp(){  
        //创建一个DatagramSocket对象,并指定监听端口。(UDP使用DatagramSocket)    
        DatagramSocket socket;  
        try {  
            socket = new DatagramSocket(9609);  
            //创建一个byte类型的数组,用于存放接收到得数据    
            byte data[] = new byte[4*1024];    
            //创建一个DatagramPacket对象,并指定DatagramPacket对象的大小    
            DatagramPacket packet = new DatagramPacket(data,data.length);    
            //读取接收到得数据    
            socket.receive(packet);    
            //把客户端发送的数据转换为字符串。    
            //使用三个参数的String方法。参数一:数据包 参数二:起始位置 参数三:数据包长    
            String result = new String(packet.getData(),packet.getOffset() ,packet.getLength());    
        } catch (SocketException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }    
    }

    Tips:

    receive()与serversocket的accepet()类似,在没有数据进行接收的处于堵塞状态。

    客户端

    Step1:创建DatagramSocket对象,并且指点监听的端口

    socket = new DatagramSocket(9609);

    Step2:创建一个InetAddress对象,这个对象类似与一个网络的发送地址

    InetAddressserver address=InetAddress.getByName("192.168.22.10");

    Step3:定义要发送的一个字符串,创建一个DatagramPacket对象,并制定要将这个数据报包发送到网络的那个地址以及端口号

    Stringstr="芳芳-我爱你";
    byte data[]=str.getByte();
    DatagramPacket packet=new DatagramPacket(data,data.length,serveraddress,9609);

    Step4:最后使用DatagramSocket对象的send()发送数据

    socket.send(packet);

    ###Java客户端发送数据方法封装代码参考(注释已经写得很详细了)

    protected void connectServerWithUDPSocket() {  
        DatagramSocket socket;  
        try {  
            //Step1:创建DatagramSocket对象并指定一个端口号  
            //如果客户端需要接收服务器的返回数据,需要使用这个端口号来receive 
            socket = new DatagramSocket(9609);  
            //Step2:InetAddress(Inet4Address).getByName把IP地址转换为网络地址    
            InetAddress serverAddress = InetAddress.getByName("192.168.22.10");  
            //Inet4Address serverAddress = (Inet4Address) Inet4Address.getByName("192.168.22.10");
    		//Step3:设置要发送的报文,    把字符串str字符串转换为字节数组
            String str = "[udp报文;udp包文]";
            byte data[] = str.getBytes();  
            //创建一个DatagramPacket对象,用于发送数据。    
            //参数1:要发送的数据  参数2:数据的长度  参数3:服务端的网络地址  参数4:服务器端端口号   
            DatagramPacket packet = new DatagramPacket(data, data.length ,serverAddress ,9609);  
    		//Step4:把数据发送到服务端。		
            socket.send(packet);    
        } catch (SocketException e) {  
            e.printStackTrace();  
        } catch (UnknownHostException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }    
    }

    同样Java客户端接收数据方法封装可以参考

    public void receiveServerSocketUDPData() {  
        DatagramSocket socket;  
        try {  
            //实例化的端口号要和发送时的socket一致,否则收不到data  
            socket = new DatagramSocket(9609);  
            byte data[] = new byte[4 * 1024];  
            //参数1:要接受的data 参数2:data的长度  
            DatagramPacket packet = new DatagramPacket(data, data.length);  
            socket.receive(packet);  
            //把接收到的data转换为String字符串  
            String result = new String(packet.getData(), packet.getOffset(),  
                    packet.getLength());  
            socket.close();//不使用了记得要关闭  
            System.out.println("the number of reveived Socket is  :" + flag  
                    + "udpData:" + result);  
        } catch (SocketException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }

    ###python中是如何通过UDP协议传输数据。和Java类似,python使用UDP的通信双方也按照分为客户端和服务器。服务器首先需要绑定端口:

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 绑定端口:

    s.bind(('127.0.0.1', 9609))

    创建Socket时,SOCK_DGRAM指定了这个Socket的类型是UDP。绑定端口和TCP一样,但是不需要调用listen()方法,而是直接接收来自任何客户端的数据:

    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 绑定端口:
    s.bind(('127.0.0.1', 9609))
    print('Bind UDP on 9609 Port...')
    while True:
        # 接收数据:
        data, addr = s.recvfrom(1024)
        print('Received from %s:%s.' % addr)
        s.sendto(b'Hello, %s!' % data, addr)

    recvfrom()方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端。

    Tips:

    这里省掉了多线程,因为这是个很简单的例子(下一篇文章:【实践2】会加上多线程)

    客户端使用UDP时,首先仍然创建基于UDP的Socket,但是不需要调用connect(),直接通过sendto()给服务器发数据:

    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    for data in [b'李##', b'孙##', b'xiaotuanzi']:
        # 发送数据:
        s.sendto(data, ('127.0.0.1', 9609))
        # 接收数据:
        print(s.recv(1024).decode('utf-8'))
    s.close()

    从服务器接收数据仍然调用recv()方法。

    仍然用两个命令行分别启动服务器和客户端测试,结果如下:

    17a1e88024b6fd547e99c538a872dcab.png
    udp通信成功,但出现编码错误截图

    左边是udp服务器,右边是upd客户端,但是看到报错了,因为所只能是ascii码,这里需要处理一下数据编码问题,暂时先把“李##”中文改为:“li##”(下一篇文章【实践2】会总结编码问题)

    b49b7bf024e9c8e03a852ca4c0acb485.png
    udp简单通信成功截图

    大功告成,可以看到服务器收到了客户端的数据,并且发送了Hello到客户端。

    四:Socket函数

    对上面Socket简单数据传输实现,Java和Python中Socket函数的简单整理,下面分别介绍

    Java Socket函数

    1. 读取数据

    上面我们使用InputStream 、 InputStreamReader和BufferedReader处理数据流读取,现在单独介绍一下三个类的不同之处以及各自的用法

    InputStream : 是所有字节输入流的超类,一般使用它的子类:FileInputStream等,它能输出字节流
    InputStreamReader : 是字节流与字符流之间的桥梁,能将字节流输出为字符流,并且能为字节流指定字符集,可输出一个个的字符
    BufferedReader : 提供通用的缓冲方式文本读取,readLine读取时一次读一行, 从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取

    InputStream

    /**
     * InputStream读取数据:通过URL连接获取InputStream,通过read方法来一个字节一个字节的读取字节流并组合在一起
     */
    private String getStream(String url){
    	InputStream in = null;
    	String result = "";
    	try {
    		in = new URL(url).openStream();
    		int tmp;
    		while((tmp = in.read()) != -1){
    			result += (char)tmp;
    		}
    	} catch (MalformedURLException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	return result;
    }

    InputStreamReader

    /*
     * InputStreamReader读取数据
     */
    private String getStream(String url){
    	try {
    		InputStream in = new URL(url).openStream();
    		InputStreamReader isr = new InputStreamReader(in,"UTF-8");
    		String results = "";
    		int tmp;
    		while((tmp = isr.read()) != -1){
    			results += (char)tmp;
    		}
    		return results;
    
    	} catch (MalformedURLException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	return null;
    }

    BufferedReader

    /*
     * BufferedReader读取数据
     */
    private String getStream(String url){
    	try {
    		InputStream in = new URL(url).openStream();
    		InputStreamReader isr = new InputStreamReader(in,"UTF-8");
    		BufferedReader bf = new BufferedReader(isr); 
    		String results = "";
    		String newLine = "";
    		while((newLine = bf.readLine()) != null){
    			results += newLine+"n";
    		}
    		return results;
    
    	} catch (MalformedURLException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	return null;
    }

    获取字符流后,可直接缓存,然后从缓存区取出,这时的速度比InputStreamReader又将快上很多。

    Tips:

    写数据OutputStreamWriter,PrintWriter,Writer以后应该writer.flush()一下

    2. Socket方法属性

    Java中Socket较完整初始化配置参考

    // 客户端 Socket 可以通过指定 IP 地址或域名两种方式来连接服务器端,实际最终都是通过 IP 地址来连接服务器  
    // 新建一个Socket,指定其IP地址及端口号  
    Socket clientSocket = new Socket("192.168.22.19",9609);  
    // 客户端socket在接收数据时,有两种超时:1. 连接服务器超时,即连接超时;2. 连接服务器成功后,接收服务器数据超时,即接收超时  
    // 设置 socket 读取数据流的超时时间  
    clientSocket.setSoTimeout(4000);  
    // 发送数据包,默认为 false,即客户端发送数据采用 Nagle 算法;  
    //  注:对于实时交互性高的程序,建议其改为 true,即关闭 Nagle 算法,客户端每发送一次数据,无论数据包大小都会将这些数据发送出去  
    clientSocket.setTcpNoDelay(true);  
    // 设置客户端 socket 关闭时,close() 方法起作用时延迟100秒关闭,如果100秒内尽量将未发送的数据包发送出去  
    clientSocket.setSoLinger(true, 100);  
    // 设置输出流的发送缓冲区大小,默认是4KB,即4096字节  
    clientSocket.setSendBufferSize(4096);  
    // 设置输入流的接收缓冲区大小,默认是4KB,即4096字节  
    clientSocket.setReceiveBufferSize(4096);  
    // 作用:每隔一段时间检查服务器是否处于活动状态,如果服务器端长时间没响应,自动关闭客户端socket  
    // 防止服务器端无效时,客户端长时间处于连接状态  
    clientSocket.setKeepAlive(true);  
    // 客户端向服务器端发送数据,获取客户端向服务器端输出流  
    OutputStream osSend = clientSocket.getOutputStream();  
    OutputStreamWriter osWrite = new OutputStreamWriter(osSend);  
    BufferedWriter bufWrite = new BufferedWriter(osWrite);  
    // 代表可以立即向服务器端发送单字节数据  
    clientSocket.setOOBInline(true);  
    // 数据不经过输出缓冲区,立即发送  
    clientSocket.sendUrgentData(0x44);//"D"  
    // 向服务器端写数据,写入一个缓冲区  
    // 注:此处字符串最后必须包含“rnrn”,告诉服务器HTTP头已经结束,可以处理数据,否则会造成下面的读取数据出现阻塞  
    bufWrite.write("xiaotuanzi#sunst@我爱你0609 rnrn");  
    // 发送缓冲区中数据 - 发送数据必须要调用一下flush(),把缓冲区的数据刷的一下就发送出去了!  
    bufWrite.flush();  

    其中重要的有2点:1是发送数据粘包处理2是读写数据时最后必须包含"rnrn",告诉服务器HTTP头已经结束,可以处理数据了。

    粘包处理

    粘包就是发送数据时是两个单独的包、两次发送,但接收时两个包连在一起被一次接收到,发送数据包,

    clientSocket.setTcpNoDelay(true);

    默认为 false,即客户端发送数据采用Nagle算法;

    对于实时交互性高的程序,建议其改为true,关闭Nagle算法,客户端每发送一次数据,无论数据包大小都会将这些数据发送出去

    Tips:

    无论 Socket 如何设置,接收方是一定要处理粘包的问题的。即在接收时,对接收到的数据进行分析,看是否存在数据不全或粘包的现象

    换行rnrn

    在从Socket的InputStream中接收数据时,若使用BufferedReader来读取数据,该方法是阻塞的,readLine是一次读一行,直到它读到了一行数据为止程序才会继续往下执行,

    那么readLine什么时候才会读到一行呢?直到程序遇到了换行符或者是对应流的结束符readLine方法才会认为读到了一行,才会结束其阻塞,让程序继续往下执行。

    所以我们在使用BufferedReader的readLine读取数据的时候一定要记得在对应的输出流里面一定要写入换行符(流结束之后会自动标记为结束,readLine可以识别)[readLine("XXn"]。

    同样的写入数据write也是一样要写入换行符[如write("XXn"],写入换行符之后一定记得如果输出流不是马上关闭的情况下记得flush一下,这样数据才会真正的从缓冲区里面写入,也可以直接使用PrintWriter类的println方法,该方法默认由程序在结尾处加入分隔符。。

    • BufferedReader的readLine
    • BufferedWriter的write
    • PrintWriter的println默认r

    3. 编码问题

    说来话长:在完成本篇[跨进程通信之Socket-理论1]时已经简单验证并总结过

    655bfcc2011bbfcd0f3e923fdb48385d.png
    处理乱码总结-草稿截图
    总的来说:统一编码是解决任何系统乱码的正确方法

    8a11673a1ba54b7b99739b6b36cd45e4.png
    Socket乱码处理UTF-8

    后面有时间本神会补充一篇,通用的乱码解决方案,这个估计得延后很久了

    关于Sokcet乱码可以先参考这里:click

    Python Socket函数

    Socket方法属性,Python官方Socket的函数可点击[Python官方Socket函数]查看

    下面是Python中Socket较完整初始化配置参考

    Python中Socket较完整初始化配置
    
    sk.bind(address)将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
    
    sk.listen(backlog)开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
    
    backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5,这个值不能无限大,因为要在内核中维护连接队列
    
    sk.setblocking(bool)是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。
    
    sk.accept()接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。接收TCP 客户的连接(阻塞式)等待连接的到来
    
    sk.connect(address)连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
    
    sk.connect_ex(address)同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
    
    sk.close()关闭套接字
    
    sk.recv(bufsize[,flag])接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
    
    sk.recvfrom(bufsize[.flag])与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
    
    sk.send(string[,flag])将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
    
    sk.sendall(string[,flag])将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
    Tips:必须转换为字节形式bytes发送,内部通过递归调用send,将所有内容发送出去
    
    sk.sendto(string[,flag],address)将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
    
    sk.settimeout(timeout)设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
    
    sk.getpeername()返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
    
    sk.getsockname()返回套接字自己的地址。通常是一个元组(ipaddr,port)
    
    sk.fileno()套接字的文件描述符

    其中讲一下socket send与sendall的区别与使用方法

    在python socket编程中,有两个发送TCP的函数,send()与sendall(),区别如下:

    • socket.send(string[, flags]) 发送TCP数据,返回发送的字节大小。这个函数执行一次,并不一定能发送完给定的数据,可能需要重复多次才能发送完成。
    • socket.sendall(string[, flags]) 发送完整的TCP数据,成功返回None,失败抛出异常

    Tips:

    同Java一样,在send时一定要写入换行符(流结束之后会自动标记为结束,readLine可以识别)[如write("XXXn"],这样服务端readline,recv时才不会阻塞

    最后,总结:

    使用UDP方式客户端和服务器端接收可以看出,其实客户端和服务器端的发送和接收类似,只要端口号正确了,相互通信就没有问题,不需要建立连接。

    此外,服务器绑定UDP端口和TCP端口互不冲突,也就是说,UDP的9999端口与TCP的9999端口可以各自绑定。TCP使用的是流的方式发送,UDP是以包的形式发送。


    以上便是跨进程通信之Socket-理论的全部内容,涉及到计算机网络,网络编程,网络协议,这可能是我目前总结过的最长的一篇文章了,

    原本我是想通过两篇文章分别介绍Java和python跨进程之Socket通信,但是我觉得跨进程通信不应该局限于某一门语言,可以是C,Java,python,OC,而且下一篇文章要实现真正跨平台的进程通信,所以有必要串联起python跟java通过一篇文章来介绍。即使如此,排版我尽量标注突出Java,python代码,让看起来不那么无厘头,所有如果没有相关语言基础的同学可以选择性吸收,当然我也建议大家多学学其它编程语言

    希望本篇[跨进程通信之Socket-理论1]能帮到大家理解,然后文章就放到人工智能AI专栏下面,因为更符合我在[Artificial Intelligence浅谈一下未来的方向]一文中说的往回走巩固python基础。

    下一篇文章-【跨进程通信之Socket-项目实践2】敬请期待,本专栏长期更新,可以点击关注

    晴雨2019-04-15 21:59

    展开全文
  • 知乎-IO 多路复用是什么意思 3.Redis到底是多线程还是单线程?线程安全吗,还需要加锁吗? 4.Redis IO多路复用技术以及epoll实现原理 5.并发编程 6.Redis单线程?别逗了,Redis6.0多线程重磅来袭!

    Redis作为一种Key-Value形式的NoSQL,因其极高的读写速度深受开发者喜爱,在web、分布式等领域有非常广泛的应用。

    根据runoob的介绍, Redis能读的速度是110000次/s,写的速度是81000次/s。

    Redis的快只是因为它是基于内存的吗?这里有一篇详细的文章对比了目前最流行的两种NoSQL—— Redis和MongoDB的性能,Redis vs. MongoDB: Comparing In-Memory Databases with Percona Memory Engine,其中MongoDB使用了Percona内存引擎,因此可以认为实验中的MongoDB和Redis (3.2.8)都是基于内存读写的(不然就没有比较的意义了)。文中做了三组对比实验,分别是:

    • 1.数据插入 —— 100%写,数据量为250万
    • 2.任务A —— 重读写,50% 读 + 50% 写,250万次操作
    • 3.任务B —— 重读取,95%读 + 5% 写,250万次操作

    实验结果分别如下:

    2159705d2ce924aaa3a3c31a83999596.png

    511bf2ca5611abd2f1888ed495794e56.png

    f74b11845d6e0aedd531cd410e0aa762.png

    可以发现,即使是利用了所有CPU核心的mongoDB,吞吐量在大多场景中仍然比不上单线程的Redis。

    所以Redis为什么这么快呢?总结起来大约以下三点:

    • 1.基于内存
    • 2.单线程
    • http://3.IO 多路复用

    Redis 6.0引入了多线程机制,性能将会有进一步的提高,可参考Redis 6.0 多线程来袭。但本文不讨论这一点,主要讨论为什么在单线程的情况下,Redis为什么仍然能做到这么高的读写?

    单线程的优势

    多线程的效率一定比单线程更高吗?

    熟悉Java的同学应该知道,Java中与Hash相关的集合有HashMap,HashTable,ConcurrentHashMap等,其中HashMap是线程不安全的,就是如果多线程环境中有多个线程同时操作该集合的话,可能导致数据污染的问题,因而有了HashTable的出现。但是在实际操作中,发现HashTable的性能比较低,因为HashTable靠一把锁来维护全部的数据,所有的线程在操作数据时只能去竞争那一把锁,造成线程等待,故而性能降低。为了克服这个问题,在JDK1.5~1.7版本,Java使用了分段锁机制实现ConcurrentHashMap。这就是多线程中锁机制造成的性能降低,此外,还有上下文切换的代价。

    CPU的每个核在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换。由于可能当前线程的任务并没有执行完毕,所以在切换时需要保存线程的运行状态,以便下次重新切换回来时能够继续切换之前的状态运行。每一次切换都是需要花时间的,所以在计算密集型的任务中,多线程(或多进程)的数量不宜超过CPU的核心数,超过之后CPU只是会徒劳的花时间在上下文切换上。

    而Redis使用单线程,就不需要考虑上述问题了。

    IO多路复用

    然而单线程的问题也很大,由于线程的执行过程是顺序执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务。为了解决这个问题,因此有了IO多路复用。

    I/O多路复用的核心是用户进程调用select函数被阻塞,同时内核轮询监听所有select 负责的socket,当任何一个socket有数据准备好了,select就将控制权返回用户进程。多路复用的具体实现方式有select / poll / epoll等,Redis内部实现采用epoll,采用了epoll+自己实现的简单的事件框架,。相比select和poll,epoll有以下几个优势:

    • epoll没有最大连接数限制 (select 的最大限制为1024)
    • Epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll,参考下图:

    2c774b95f6d73775869c71eea2be24e1.png
    • 内存拷贝, select 和poll 在数据准备好之后,需要把数据从内核拷贝到用户进程,Epoll使用了“共享内存”,避免了内存拷贝。
    • epoll线程安全

    总结

    Redis 使用了I/O多路复用,保证了redis在进行I/O操作时依然能处理socket请求,不会在I/O上浪费时间,单线程机制也避免了不必要的上下文切换和锁机制。而Redis的每一次I/O操作都是基于内存的,非常高效。可以说,redis的这三个特性相辅相成,共同造就了Redis的高并发。

    参考

    1.Redis vs. MongoDB: Comparing In-Memory Databases with Percona Memory Engine

    2.知乎-IO 多路复用是什么意思

    3.Redis到底是多线程还是单线程?线程安全吗,还需要加锁吗?

    4.Redis IO多路复用技术以及epoll实现原理

    5.并发编程

    6.Redis单线程?别逗了,Redis6.0多线程重磅来袭!

    展开全文
  • 同学,请看看你自己的电视和什么连在一起。...谁能告诉我中文里套接字是什么意思?接线板(Socket)机制很形象。服务器和客户端都生成一个Socket的类对象,俩对象就可以发送数据了。当然,机器之间的物理硬件...

    同学,请看看你自己的电视和什么连在一起。是木桌子吗?还是窗帘?对,是接线板。网络的发明者们就是受到了我刚才提到的那个大家熟视无睹的现象的启发而采取了Socket(接线板)机制。但不幸的是,我们的最早的翻译家们不知什么原因,翻译成了套接字?谁能告诉我中文里套接字是什么意思?接线板(Socket)机制很形象。服务器和客户端都生成一个Socket的类对象,俩对象就可以发送数据了。当然,机器之间的物理硬件以及电缆连接等细节都封装在类中了。我们就没必要关心了。下一步,从Socket接线板中得到一个InputStream或OutputStream,这样,我们就可以将网络连接作为一对IO流对象来处理了。
    更多请见:http://www.mark-to-win.com/tutorial/java_9_HowSocketWork.html

    展开全文
  • 避免了频繁的上下文切换(三)采用了非阻塞I/O多路复用机制题外话:我们现在要仔细的说一说I/O多路复用机制,因为这个说法实在是太通俗了,通俗到一般人都不懂是什么意思。博主打一个比方:小曲在...
  • socket什么意思

    2016-07-20 21:06:30
    在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。 socket 的典型应用就是...
  • 玩玩socket-socket什么意思

    千次阅读 2019-06-12 17:29:53
    在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。 socket 的典型应用就是 ...
  • 小程序也给我们提供了另一种通讯方式是基于websocket请求的,那么http请求和websocket它们之间的含义以及有什么什么不同呢?下来就让我们一起来看下吧:我们可以看到http协议呢是基于TCP/IP通信协议通过万维网服务器...
  • 1、socket什么意思

    千次阅读 2017-05-21 13:47:45
    在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。 socket 的典型应用就是 ...
  • 展开全部socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应e69da5e887aa62616964757a686964616f31333337396231答网络请求。-----J2SDK-1.3为例,...
  • socket什么意思(1)

    万次阅读 多人点赞 2016-04-12 09:23:54
    在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。 socket 的典型应用就是 ...
  • socket 调用 connect连接 WSAGetLastError 返回错误码36 是什么意思
  • socket中FAR*是什么意思

    2015-05-09 16:07:13
    比如 int select( int nfds, fd_set FAR*readfds, fd_set FAR*writefds, fd_set FAR*exceptfds, const struct timeval FAR*timeout );... fd_set是个结构体,后面的FAR*是什么意思
  • 那么“状态机”是什么意思?一句话的基本定义状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门,就有 open 和 ...
  • socket长连接是什么意思 短连接:1 次 socket 连接后,只进行 1 次 HTTP 事务,然后断开 socket 连接; 长连接:1 次 socket 连接后,不管是否使用 socket 连接(进行多次 HTTP 事务),不断开 socket 连接。 参考...
  • 那么梦见很久不联系的前女友是什么意思呢?下面就跟随周公解梦一起来看看吧。 梦见很久不联系的前女友的梦境解析 梦见很久不联系的前女友,预示梦者感情运佳,单身者将遇到命中注定的良缘,不要错过为佳;有伴者则...
  • 既然我们知道了分销的概念和内部的意义,那么酒店分销是什么意思呢?其实我们可以这样来进行介绍,所谓的酒店,其实就是服务业,它的分销也同样利用一个网络建立一个分销预定的平台。 经营者并非酒店(酒店经销商)它...
  • 那么为了能够更加深入去了解到有关于一个人的发展的状态,我们一起来看看,到底女的右手手心有痣是什么意思?有什么方面的运势特点预示? 往往感情会遇到挫折 女的右手手心有痣,往往感情方面容易遇到挫折的。而也是...
  • 为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口,区分不同应用程序进程间的网络通信和连接。 网络化的应用程序在开始任何通讯之前都必需要创建...
  • 你要永远相信光是什么意思?维护正义的化身,一道光就能召唤。对于男生的信仰,不能去进行磨灭,所以得了解啊。你要永远相信光是什么梗其实是和奥特曼有关的 ,奥特曼代表的是光明 ,它的出现就是保护世界、保卫和平...
  • 简单来说S5就是socket5代理的缩写,一种由中转服务服务器架构的,用大白话来说,就是你要是送某样东西,但是路程特别远,你没有办法去处理,这时候,肯定是需要用到快递,帮助你送这样东西,这样实现的过程就是S5...
  • Python 提供了两个基本的 socket 模块。第一个是 Socket,它提供了标准的 BSD Sockets API。第二个是 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。下面讲的是Socket模块功能1、Socket类型套接...
  • 为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口,区分不同应用程序进程间的网络通信和连接。 网络化的应用程序在开始任何通讯之前都必需要创建...
  • 什么socket

    2020-07-25 12:05:23
    无论是什么语言,都有socket的实现,但是对于初学者,我们有时候都不知道socket什么意思,下面解释一下socket,说的直白一点:socket = (IP:port),socket就是ip地址加上端口号,socket概念是传输层提出来的,...
  • Socket serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"),2020); serverSocket.Bind(server...
  • 在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。 socket 的典型应用就是 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 534
精华内容 213
关键字:

socket什么意思