什么是 Socket?

Socket 的中文翻译过来就是“套接字”。套接字是什么,我们先来看看它的英文含义:插座。

Socket 就像一个电话插座,负责连通两端的电话,进行点对点通信,让电话可以进行通信,端口就像插座上的孔,端口不能同时被其他进程占用。而我们建立连接就像把插头插在这个插座上,创建一个 Socket 实例开始监听后,这个电话插座就时刻监听着消息的传入,谁拨通我这个“IP 地址和端口”,我就接通谁。

实际上,Socket 是在应用层和传输层之间的一个抽象层,它把 TCP/IP 层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。Socket 起源于 UNIX,在 UNIX 一切皆文件的思想下,进程间通信就被冠名为文件描述符(file descriptor),Socket 是一种“打开—读/写—关闭”模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

另外我们经常说到的Socket 所在位置如下图:

Socket 通信过程

Socket 保证了不同计算机之间的通信,也就是网络通信。对于网站,通信模型是服务器与客户端之间的通信。两端都建立了一个 Socket 对象,然后通过 Socket 对象对数据进行传输。通常服务器处于一个无限循环,等待客户端的连接。

一图胜千言,下面是面向连接的 TCP 时序图

客户端过程:

客户端的过程比较简单,创建 Socket,连接服务器,将 Socket 与远程主机连接(注意:只有 TCP 才有“连接”的概念,一些 Socket 比如 UDP、ICMP 和 ARP 没有“连接”的概念),发送数据,读取响应数据,直到数据交换完毕,关闭连接,结束 TCP 对话。

import socket
import sys if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 Socket 连接
sock.connect(('127.0.0.1', 8001)) # 连接服务器
while True:
data = input('Please input data:')
if not data:
break
try:
sock.sendall(data)
except socket.error as e:
print('Send Failed...', e)
sys.exit(0)
print('Send Successfully') res = sock.recv(4096) # 获取服务器返回的数据,还可以用 recvfrom()、recv_into() 等
print(res)
sock.close()

sock.sendall(data)

这里也可用 send() 方法:不同在于 sendall() 在返回前会尝试发送所有数据,并且成功时返回 None,而 send()则返回发送的字节数量,失败时都抛出异常。

服务端过程:

咱再来聊聊服务端的过程,服务端先初始化 Socket,建立流式套接字,与本机地址及端口进行绑定,然后通知 TCP,准备好接收连接,调用 accept() 阻塞,等待来自客户端的连接。如果这时客户端与服务器建立了连接,客户端发送数据请求,服务器接收请求并处理请求,然后把响应数据发送给客户端,客户端读取数据,直到数据交换完毕。最后关闭连接,交互结束。

import socket
import sys if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 Socket 连接(TCP)
print('Socket Created') try:
sock.bind(('127.0.0.1', 8001)) # 配置 Socket,绑定 IP 地址和端口号
except socket.error as e:
print('Bind Failed...', e)
sys.exit(0) sock.listen(5) # 设置最大允许连接数,各连接和 Server 的通信遵循 FIFO 原则 while True: # 循环轮询 Socket 状态,等待访问
conn, addr = sock.accept()
try:
conn.settimeout(10) # 如果请求超过 10 秒没有完成,就终止操作 # 如果要同时处理多个连接,则下面的语句块应该用多线程来处理
while True: # 获得一个连接,然后开始循环处理这个连接发送的信息
data = conn.recv(1024)
print('Get value ' + data, end='\n\n')
if not data:
print('Exit Server', end='\n\n')
break
conn.sendall('OK') # 返回数据
except socket.timeout: # 建立连接后,该连接在设定的时间内没有数据发来,就会引发超时
print('Time out') conn.close() # 当一个连接监听循环退出后,连接可以关掉
sock.close()conn, addr = sock.accept()

调用 accept() 时,Socket 会进入waiting状态。客户端请求连接时,方法建立连接并返回服务器。accept() 返回一个含有两个元素的元组 (conn, addr)。第一个元素 conn 是新的 Socket 对象,服务器必须通过它与客户端通信;第二个元素 addr 是客户端的 IP 地址及端口。data = conn.recv(1024)

接下来是处理阶段,服务器和客户端通过 send() 和 recv() 通信(传输数据)。
服务器调用 send(),并采用字符串形式向客户端发送信息,send() 返回已发送的字符个数。
服务器调用 recv() 从客户端接收信息。调用 recv() 时,服务器必须指定一个整数,它对应于可通过本次方法调用来接收的最大数据量。recv() 在接收数据时会进入blocked状态,最后返回一个字符串,用它表示收到的数据。如果发送的数据量超过了 recv() 所允许的,数据会被截短。多余的数据将缓冲于接收端,以后调用 recv() 时,会继续读剩余的字节,如果有多余的数据会从缓冲区删除(以及自上次调用 recv() 以来,客户端可能发送的其它任何数据)。传输结束,服务器调用 Socket 的 close() 关闭连接。

从 TCP 连接的视角看 Socket 过程:

TCP 三次握手的 Socket 过程:

  1. 服务器调用 socket()bind()listen() 完成初始化后,调用 accept() 阻塞等待;
  2. 客户端 Socket 对象调用 connect() 向服务器发送了一个 SYN 并阻塞;
  3. 服务器完成了第一次握手,即发送 SYN 和 ACK 应答;
  4. 客户端收到服务端发送的应答之后,从 connect() 返回,再发送一个 ACK 给服务器;
  5. 服务器 Socket 对象接收客户端第三次握手 ACK 确认,此时服务端从 accept() 返回,建立连接。

接下来就是两个端的连接对象互相收发数据。

TCP 四次挥手的 Socket 过程:

  1. 某个应用进程调用 close() 主动关闭,发送一个 FIN;
  2. 另一端接收到 FIN 后被动执行关闭,并发送 ACK 确认;
  3. 之后被动执行关闭的应用进程调用 close() 关闭 Socket,并也发送一个 FIN;
  4. 接收到这个 FIN 的一端向另一端 ACK 确认。

总结:

上面的代码简单地演示了 Socket 的基本函数使用,其实不管有多复杂的网络程序,这些基本函数都会用到。上面的服务端代码只有处理完一个客户端请求才会去处理下一个客户端的请求,这样的服务器处理能力很弱,而实际中服务器都需要有并发处理能力,为了达到并发处理,服务器就需要 fork 一个新的进程或者线程去处理请求。

   学习思路这是很重要的 

点此加入该群

什么是PHP Socket?的更多相关文章

  1. socket读写返回值的处理

    在调用socket读写函数read(),write()时,都会有返回值.如果没有正确处理返回值,就可能引入一些问题 总结了以下几点 1当read()或者write()函数返回值大于0时,表示实际从缓冲 ...

  2. Socket聊天程序——Common

    写在前面: 上一篇记录了Socket聊天程序的客户端设计,为了记录的完整性,这里还是将Socket聊天的最后一个模块--Common模块记录一下.Common的设计如下: 功能说明: Common模块 ...

  3. Socket聊天程序——客户端

    写在前面: 上周末抽点时间把自己写的一个简单Socket聊天程序的初始设计和服务端细化设计记录了一下,周二终于等来毕业前考的软考证书,然后接下来就是在加班的日子度过了,今天正好周五,打算把客户端的详细 ...

  4. Socket聊天程序——服务端

    写在前面: 昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图: ...

  5. Socket聊天程序——初始设计

    写在前面: 可能是临近期末了,各种课程设计接踵而来,最近在csdn上看到2个一样问答(问题A,问题B),那就是编写一个基于socket的聊天程序,正好最近刚用socket做了一些事,出于兴趣,自己抽了 ...

  6. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  7. Android Socket连接PC出错问题及解决

    最近测试问题:Android 通过Socket链接电脑,ip和端口都是正确的,也在同一网段,可android端就是报异常如下: 解决办法:测试电脑的防火墙可能开着,在控制面板把防火墙打开即可.

  8. Linux下的C Socket编程 -- server端的继续研究

    Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...

  9. Mono 3.2.3 Socket功能迎来一稳定的版本

    由于兴趣自己业余时间一直在搞.net下面的通讯应用,mono的存在得以让.NET程序轻松运行在Linux之下.不过经过多尝试Socket相关功能在Mono下的表现并不理想.不管性能还是吞吐能力方面离我 ...

  10. Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!

    随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...

随机推荐

  1. Linux常用命令-不定时记录

    文件移动命令 命令格式:mv [-fiv] source destination 参数说明:-f:force,强制直接移动而不询问-i:若目标文件(destination)已经存在,就会询问是否覆盖- ...

  2. 爬虫链接mongodb 以及多线程多进程的操作

    一.连接mongodb 1.            设置数据库 client=pymongo.MongoClient(‘localhost’) 2.            db=client[‘lag ...

  3. SpringBoot整合Redis(一)

    docker启动redis docker run -p 6379:6379 --name myredis redis 查看容器 [root@topcheer ~]# docker ps -l CONT ...

  4. QTCreator配置调试参数

    1. 调试参数在“Projects”的配编译参数那儿,编译参数build的旁边run中 2. 加断点是在文件行数左边那个地方

  5. 开源.Net Standard版华为物联网北向接口SDK

    最近用到了华为的物联网平台API,但是官方没有.Net版的SDK,所以就自己封装了一个,开源出来给有需要的朋友,同时也算是为.Net Core的发展做点小贡献~ 源码地址:https://github ...

  6. 在线API文档管理工具Simple doc

    Simple doc是一个简易的文档发布管理工具,为什么要写Simple doc呢?主要原因还是github的wiki并不好用:没有目录结构,文章没有Hx标签索引,最悲剧的是文章编辑的时候不能直接图片 ...

  7. 爬取bing背景图片

    因为工作环境的原因,没办法用梯子,也不喜欢用某度,只能用bing,发现背景图片蛮好看的,刚好最近在学习摄影,需要提高审美,就想着把bing背景图片都爬去下来做桌面背景.写的代码比较入门,只是做个记录, ...

  8. 使用Typescript重构axios(十二)——增加参数

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  9. 从BIO到Netty的演变

    从BIO到Netty的演变 前言 计算机网络可以说是每个学计算机的都绕不过去的一道坎.计算机网络到底有多么重要,你走到大学图书馆的计算机部分,翻开那些什么<从零开始:黑客XXX>,< ...

  10. unittest使用数据驱动ddt

    简介 ddt(data driven test)数据驱动测试:由外部数据集合来驱动测试用例,适用于测试方法不变,但需要大量变化的数据进行测试的情况,目的就是为了数据和测试步骤的分离 由于unittes ...