Socket 学习笔记

参考文档:《linux网络编程》

Socket 的英文原意就是“孔”或“插座” ,现在,作为 BSD UNIX 的进程通讯机制,取其后一种意义。将电话系统与面向连接的 Socket 机制相比,有着惊人相似的地方。以一个国家级的电话网为例。电话的通话双方相当于相互通信的两个进程;通话双方所在的地区(享有一个全局唯一的区号)相当于一个网络,区号是它的网络地址;区内的一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于 Socket 号。

一个完整的 Socket 则用一个相关描述{协议,本地地址,本地端口,远程地址,远程端口}。每一个 Socket 有一个本地的唯一 Socket 号,由操作系统分配。

最重要的是,Socket 是面向客户-服务器模型而设计的,针对客户和服务器程序提供不同的 Socket 系统调用。客户随机申请一个 Socket 号(相当于一个想打电话的人可以在任何一台入网的电话上拨叫呼叫) ;服务器拥有全局公认的 Socket,任何客户都可以向它发出连接请求和信息请求(相当于一个被呼叫的电话拥有一个呼叫方知道的电话号码)。

Socket 学习笔记_第1张图片

socket 接口示意图

套接字有三种类型:流式套接字 (SOCK_STREAM), 数据报套接字 (SOCK_DGRAM)及原始套接字。

流式套接字(SOCK_STREAM)

流式的套接字可以提供可靠的、面向连接的通讯流。如果你通过流式套接字发送了顺序的数据: “1”、 “2” 。那么数据到达远程时候的顺序也是“1”、 “2”。

流式套接字是怎样保证这种应用层次上的数据传输质量呢?它使用了 TCP ( TheTransmission Control Protocol )协议。TCP 保证了你的数据传输是正确的,并且是顺序的。TCP 是经常出现的 TCP/IP 中的前半部分。IP代表 Internet Protocol (因特网协议),IP 只处理网络路由

流式套接字工作过程如下: 服务器首先启动,通过调用 socket() 建立一个套接字,然后调用bind()将该套接字和本地网络地址联系在一起,再调用listen() 使套接字做好侦听的准备,并规定它的请求队列的长度, 之后就调用accept() 来接收连接。客户在建立套接字后就可调用connect() 和服务器建立连接。连接一旦建立,客户机和服务器之间就可以通过调用read() 和 write() 来发送和接收数据。最后,待数据传送结束后,双方调用close() 关闭套接字

Socket 学习笔记_第2张图片

面向连接的 socket 的工作流程

数据报套接字(SOCK_DGRAM)

数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。原始套接字允许对低层协议如 IP 或 ICMP 直接访问,主要用于新的网络协议实现的测试等

为什么数据报套接字(Datagram Sockets)叫做“无连接”?应该怎样处理它们呢?为什么它们是不可靠的?这里有一些事实:
(1)如果你发送了一个数据报,它可能不会到达。
(2)它可能会以不同的顺序到达。
(3)如果它到达了,它包含的数据中可能存在错误。

数据报套接字也使用 IP,但是它不使用 TCP,它使用使用者数据报协议 UDP(User Datagram Protocol)。

为什么说它们是“无连接”的呢?因为它(UDP)不像流式套接字那样维护一个打开的连接,你只需要把数据打成一个包,把远程的 IP 贴上去,然后把这个包发送出去。这个过程是不需要建立连接的。UDP 的应用例子有:tftp, bootp 等。

那么,数据包既然会丢失,怎样能保证程序能够正常工作呢?事实上,每个使用 UDP的程序都要有自己的对数据进行确认的协议。比如,TFTP 协议定义了对于每一个发送出去的数据包,远程在接受到之后都要回送一个数据包告诉本地程序: “我已经拿到了!” (一个  “ACK”  包) 。如果数据包发的送者在 5 秒内没有的得到回应,它就会重新发送这个数据包直到数据包接受者回送了  “ACK ”  信号。这些知识对编写一个使用 UDP 协议的程序员来说是非常必要的。

无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的相互作用。


无连接的 socket 工作流程

原始套接字

原始套接字主要用于一些协议的开发,可以进行比较底层的操作。它功能强大,但是没有上面介绍的两种套接字使用方便,一般的程序也涉及不到原始套接字。


====================================


什么是 Socket?

一个套接字可以这样来解释:它是通过标准的 UNIX 文件描述符和其他的程序通讯的一个方法。

在 UNIX 系统中,任何东西都是一个文件,在 UNIX 系统中,任何对 I/O 的操作,都是通过读或写一个文件描述符来实现的。一个文件描述符只是一个简单的整形数值,代表一个被打开的文件(这里的文件是广义的文件,并不只代表不同的磁盘文件,它可以代表一个网络上的连接,一个先进先出队列,一个终端显示屏幕,以及其他的一切)。在UNIX 系统中任何东西都是一个文件!所以如果你想通过 Internet 和另外一个程序通讯的话,你将会是通过一个文件来描述符实现的。

我们应该怎样才能得到这个代表网络连接的文件描述符呢?

首先调用系统函数 socket(),它返回一个套接字(Socket)描述符,然后就可以通过对这个套接字描述符进行一些操作:系统函数 send()  和 recv() 。

套接字描述符是一个文件描述符,为什么不能用对文件操作的 write()  和read()  来进行套接字通讯呢?

事实上,write()  和 read()  是可以对套接字描述符进行操作的,但是,通过使用 send()  和 recv()  函数,你可以对网络数据的传输进行更好的控制。


====================================


下面是 OSI 模型(抽象到物理)

(1)应用层
(2)表示层
(3)会话层
(4)传输层
(5)网络层
(6)数据链路
(7)物理层

上面这个模型是最一般的模型,但是在 Linux 中,真正用到的模型是下面这样子的:

(1)应用层(Telnet,Ftp,等等)
(2)主机间对话层(TCP 和 UDP)
(3)网络层(IP  和路由)
(4)网络底层(相当于 OSI 模型中网络、数据链路和物理层)

对流式套接字,所需要做的只是调用 send()  函数来发送数据。而对于数据报套接字,需要自己加个信息头,然后调用 sendto()  函数把数据发送出去。Linux 系统内核中已经建立了 Transport Layer Internet Layer 。硬件负责 NetworkAccess Layer


从socket “读”:用socket 接收数据
向socket “写”:用socket 发送数据


你可能感兴趣的:(Socket 学习笔记)