关于socket

本文详细探讨了Socket的工作过程,从概念、实现到类型,揭示了Socket作为进程间通信机制的本质。服务器端Socket通过创建、绑定端口、监听请求,以及使用child socket处理客户端连接,完成三次握手、通信和四次挥手的过程。文中还提供了Python和Go的网络编程实例,建议阅读《UNIX网络编程 卷1:套接字联网API(第3版)》以深入了解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对sokect的工作过程一直很模糊,特别是看到一些库的网络实现,总难免有困惑。今天决定搞清楚。

网上看到的不少资料,会从各个层面跟我们解释socket:

概念:

我们知道,两个进程通信时,要先能确定信息接收的对象。在互联网中,我们通过ip确定了目标机器,通过端口号确定了机器上的某个进程。因此协议+ip+端口能唯一确定信息接收(连接)对象。

socket是操作系统提供的进程间通信的机制。

大部分应用层的协议如FTP、SMTP、POP3等都使用socket建立进程间的连接。 当两个进程要建立双向连接进行通信时,socket往往成对存在,一个在进程A(客户端),一个在进程B(服务器端)。因此也有人说:socket是进程间通信的一个端点。

实现:

简单理解Socket一文有段话:

socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

这段话其实已经点出了socket的意义和本质。

 

类型:

Sockets Tutorial 提到应用广泛的 socket types有两种,一种是stream socket,一种是datagram socket。

实际上,socket在创建的时候,需要指定两个参数address domainsocket type。且两个进程间的通信,只有在各自socket的domain和type相同时才能进行。

address domains主要有:unix domainInternet domain

如果socket指定了unix domain,两个进程在同一个文件系统下通信。

如果socket指定了Internet domain,两个进程在网络中两台主机间通信。

Stream sockets会把通信的数据作为持续的字符串流,并使用tcp协议。

datagram sockets会一次读取整条message,使用udp协议。

工作:

socket到底如何工作,才是我最想知道的。一开始看一些中文的帖子,基本上都只是大概地说:创建socket,监听某个端口,接收到连接之后处理返回,关闭连接。

但我的疑问是:服务器进程一直在跑,肯定要有socket一直监听端口。

是处理完一个请求之后,就关闭这个socket,然后重开一个socket继续监听该端口?

还是说socket1一直监听端口,请求来了之后,创建另一个socket2去处理请求,然后关闭socket2?

Using Socket as a Server (Listening) Socket

Socket Operations

Socket programming - What's the difference between listen() and accept()?这篇问答给了我答案。

大致地说,服务器端socket经历了下面几个过程:

创建socket

调用系统bind方法让socket绑定端口

调用系统listen方法让socket开始监听请求

接收请求,创建child sokect(或称client socket)连接到来的请求

    三次握手

    child socket与客户端通信(读写)

    四次挥手

    child socket关闭

listen socket继续监听

直到listen socket 关闭,释放资源。

右边是tutorialspoint上一个完整的例子: Unix Socket - Server Examples实现:

上面的流程在不同语言中都能看到相似的实现:

下面是菜鸟教程python3网络编程的简单实例:

#导入 socket、sys 模块
import socket
import sys

# 创建 socket 对象
serversocket = socket.socket(
            socket.AF_INET, socket.SOCK_STREAM) 

# 获取本地主机名
host = socket.gethostname()

port = 9999

# 绑定端口号
serversocket.bind((host, port))

# 设置最大连接数,超过后排队
serversocket.listen(5)

while True:
    # 建立客户端连接
    clientsocket,addr = serversocket.accept()      

    print("连接地址: %s" % str(addr))
    
    msg='欢迎访问菜鸟教程!'+ "\r\n"
    clientsocket.send(msg.encode('utf-8'))
    clientsocket.close()

下面是go源码中http的处理过程,见文章3.3 Go如何使得Web工作

func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		rw, e := l.Accept() //接收客户端连接
		if e != nil {
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
                ...
				continue
			}
			return e
		}
		tempDelay = 0
		c, err := srv.newConn(rw)  
		if err != nil {
			continue
		}
		go c.serve()
	}
}

之前我一直很奇怪这里为什么accept返回是个rw,今天才直到原来是充当了socket的角色。。

其实在nodejs底层也是类似的实现,只是js层面上我们看不到太多。有兴趣可以看看走进Node.js 之 HTTP实现分析

当然,这是都只是从资料上看到的东西,真正像研究socket这部分,还是得看前辈推荐的教材:

《UNIX网络编程 卷1:套接字联网API(第3版)》

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值