Socket基础知识与底层操作的理解

初次在项目中接触到Socket编程,connect、bind、acccept、listen、recv等各个函数能用起来,但是只知其然不知其所以然。拜读《TCP/IP Sockets编程C语言实现》,小有收获,总结如下。

术语

主机(host路由器(router)通过通信信道互联形成计算机网络。路由器也称为网关(gateway),其作用是把信息(information)从一条信道中继或转发(forward)到另一条信道。信息的含义是指由程序(通常运行在主机上)构造和解释的字节序列。在计算机网络环境中,这些字节序列称为分组(packet)

协议(protocol)是关于由程序交换的分组及其含义的协定,协议说明如何构造分组以及如何解释信息。协议通常设计成使用给定的能力解决特定的问题。实现一个有用的网络需要解决大量不同的问题,为了保持事情易于管理并且是模块化的,设计了不同的协议来解决不同的问题集。TCP/IP就是这样一个解决方案的集合,也称之为协议族(protocol suite)。TCP/IP协议族中的主要协议是IP(Internet Protocol,网际协议),TCP(Transmission Control Protocol,传输控制协议)和UDP(User Datagram Protocol,用户数据报协议)。

TCP/IP网络

从上图可以看出,应用程序通过Sockets API访问UDP和TCP提供的服务。TCP/UDP使用端口号(Port Number)来确定主机内的应用程序。

将IPV4地址写为一组4个用句点隔开的十进制数字(例如192.168.0.1),这称为点分四组(dotted-quad)表示法。以10或192.168等开头的IP地址都是特殊地址,这些地址不能从全球Internet访问到它们。当使用名称标识通信端点时,系统将做一些额外的工作把名称解析为地址。名称解析服务可以访问广泛来源的信息,两种主要来源是DNS(Domain Name System,域名系统)和本地配置数据库。

套接字、协议与端口

套接字(socket)是一个抽象层,应用程序可以通过它发送和接收数据,其方式与打开文件句柄允许应用程序读、写数据到稳定的存储器非常相似。TCP/IP套接字是由Internet地址、端到端协议(TCP或UDP)、端口号唯一标识的。以前说端口标识主机上的应用程序,实际上,端口标识主机上的套接字。

sockaddr结构是Sockets API的一种泛型数据类型。对于IPv4,使用sockaddr_in结构,它只是sockaddr结构中数据的更详细的试图,是为IPv4套接字为定制的,可以把它强制转换为sockaddr。对于IPv6,使用sockaddr_in6结构。泛型sockaddr其实装不下IPv6的地址,于是创建了sockaddr_storage结构

一些概念

把消息传送到程序需要用到两段地址,即IP地址和端口号。类比邮政业务进行理解,其中IP地址就是街道地址,而端口号对应于房间号。类比电话系统,IP地址就是公司主机电话号码,端口号就是某个人的电话分机号。

在服务器-客户端的模式中,类比电话及邮政系统,客户(client)程序发起通信,服务器(server)程序被动的等待。客服必须给connect()提供服务器的地址,而服务器不需要知道对应方(peer)的地址,但服务器必须给bind()指定它自己的地址。

TCP是一种字节流协议。这类协议的一种实现是不会保持send()的边界。通过在连接的一端调用send()发送的字节可能不会通过在另一端单独调用一次recv()而全都返回。因此需要反复接收此字节,直至接收到的字节数与发送的字节数相等为止。这个循环多半只会执行一次,但是这不能保证,编写套接字程序的基本原则是:对于另一端的网络和程序将要做什么事情,永远都不能做出假设这也是使用套接字的网络应用程序的一个关键原则是防御性编程:你的代码绝对不能对通过网络接收到的任何信息作出假定。

书中提供的示例没有包含专门用于从错误中恢复的大量代码,只是简单的终止程序并退出。生产代码不应该如此轻易地放弃。

偶尔需要给用户提供信息,如果需要格式化能力,就使用printf(),否则,就使用fputs()。应该尽量避免使用printf()输出固定的、预先格式化的字符串。你永远也不应该做的一件事是:把从网络接收到的文本作为第一个参数传递给printf()。它会引起严重的安全性问题,要代之以使用fputs()。

recv()会阻塞到接收到数据或者客户关闭连接为止。当客户正常地关闭连接时,recv()返回0。如果从客户发送的数据包或者来自服务器的应答数据报丢失,我们的应答客户将会无限期地阻塞。这种情况需要使用recvfrom()解除阻塞。

accept()会阻塞,直至建立了连接以侦听套接字的端口号为止,此时,accept()为新套接字返回一个描述符。

close()告诉底层协议栈发起关闭通信以及释放与套接字关联的任何资源所需的任何动作。一旦调用close(),就会对导致错误的套接字调用其它操作,例如send()和recv()。

Socket底层的理解

如果不理解套接字的具体实现所关联的数据结构和底层协议的工作细节,就很难领会网络编程的一些精妙之处。只是简略对一些普通事件序列的基础性的理解,对于编程也是有用的。

  1. 底层数据结构
与套接字关联的数据结构

RecvQ:等待递送的所接收数据的FIFO队列。

SendQ:等待传输的数据的FIFO队列。

State:Listening、TIME_WAIT、ESTABLISHED、Closed、Connecting、Syn_Sent…

由于状态之间的过渡非常迅速,很难在netstat提供的“快照”中捕获部分中间状态。执行套接字涉及的函数,都会导致状态的变化,更详细的内容可以查看“TCP套接字的生存期”。

  1. send()与recv()时底层的状态的变化
send()了8000字节后某个时刻底层的状态
发送端netstat快照
接收端netstat快照

在TCP套接字上完成send()调用并不意味这实际地传输了数据,而只是把它们复制到了本地缓冲区SendQ中。

TCP协议负责移动字节,从SendQ中移动到RecvQ中,这种转移不受用户程序控制。

通过调用Recv()可以把字节从RecvQ中移动到Delivered中,传输的块的大小依赖于RecvQ中的数据量以及提供给recv()的缓冲区的大小。

  1. 避免死锁

设计套接字应用程序,必须小心地避免死锁(deadlock),在这种状态中,每一方都被阻塞,并等待另一方执行某种动作。例如,在建立连接后客户和服务端都立即尝试接收数据,则两端都阻塞在Recv(),导致死锁。

一种可能导致死锁的情况

如上图所示,此时RecvQ和SendQ缓冲区都是500字节,两端都尝试发送1500字节的数据,此时两端都阻塞在了send()中(因为应用层都还有500字节没有送到SendQ中)。

考虑到如上这些情况,需要仔细设计协议,尤其是要避免两个方向上同时发送大量的数据。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容