部分内容来自:https://www.jianshu.com/p/066d99da7cbd

1  七层网络架构

  在解释socket之前,先了解下七层网络架构

  https://www.cnblogs.com/jthr/p/15860454.html

 

2 socket简介

  在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据,通过它进行网络通信
  socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。

  实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。通过Socket,我们才能使用TCP/IP协议。 实际上,Socket跟TCP/IP协议没有必然的联系。Socket编程接口在设计的时候,就希望也能适应TCP/IP以外的其他的网络协议。所以说,Socket的出现只是使得程序员更方便地使用TCP/IP及其它协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、 listen、connect、accept、send、read和write等等。

  网络有一段关于socket和TCP/IP协议关系的说法比较容易理 解:
  “TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。”
  实际上,传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。

 

3 网络中进程通信

3.1、本地进程间通信

  1)消息传递(管道、消息队列、FIFO)
  2)同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  3)共享内存(匿名的和具名的,eg:channel)
  4)远程过程调用(RPC)

 

3.2、网络中进程如何通信

3.2.1 网络中进程通信的两个问题
  1)我们要如何标识一台主机,即怎样确定我们将要通信的进程是在那一台主机上运行。
  2)我们要如何标识唯一进程,本地通过pid标识,网络中应该怎样标识


3.2.2 解决办法
  1)TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机
  2)传输层的“协议+端口”可以唯一标识主机中的应用程序(进程),因此,我们利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互

 

Socket怎么通信

  现在,我们知道了网络中进程间如何通信,即利用三元组【ip地址,协议,端口】可以进行网络间通信了,那应该怎么实现了,socket应运而生,它就是利用三元组解决网络通信的一个工具,就目前而言,几乎所有的应用程序都是采用socket,如UNIX BSD的套接字(socket)。
  Socket通信的数据传输方式,常用的有两种:SOCK_STREAM和SOCK_DGRAM

 

4.1 SOCK_STREAM

  表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。

 

4.2 SOCK_DGRAM

  表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
  例如:QQ 视频聊天和语音聊天就使用 SOCK_DGRAM 传输数据,因为首先要保证通信的效率,尽量减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质的影响

 

5 TCP/IP

  https://www.cnblogs.com/jthr/p/16858278.html

 

6 典型的应用编程接口

7 Socket常用函数接口

  以WinSock为例子,其它的差不多
 
1)WSAStartup

 

2)WSACleanup

 

3)socket

 

 4)Closesocket

 

5)bind

 

6)listen

 

7)connect

 

8)accept

 

 9)send,sendto

 

10)recv,recvfrom

 

 

8 网络应用的SocketAPI(TCP)基本调用过程

 

9 socket缓冲区以及阻塞模式

9.1 socket缓冲区

  每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

  send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。

  TCP协议独立于 send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。

  recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取


  这些I/O缓冲区特性可整理如下:
1)I/O缓冲区在每个TCP套接字中单独存在;
(2)I/O缓冲区在创建套接字时自动生成;
(3)即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
(4)关闭套接字将丢失输入缓冲区中的数据。

  

9.2 阻塞模式

  对于TCP套接字(默认情况下),当使用 send() 发送数据时

1) 首先会检查缓冲区,如果缓冲区的可用空间长度小于要发送的数据,那么 send() 会被阻塞(暂停执行),直到缓冲区中的数据被发送到目标机器,腾出足够的空间,才唤醒 write()/send() 函数继续写入数据。
2) 如果TCP协议正在向网络发送数据,那么输出缓冲区会被锁定,不允许写入,send() 也会被阻塞,直到数据发送完毕缓冲区解锁,send() 才会被唤醒。
3) 如果要写入的数据大于缓冲区的最大长度,那么将分批写入。
4) 直到所有数据被写入缓冲区 send() 才能返回。

  当使用 recv() 读取数据时

1) 首先会检查缓冲区,如果缓冲区中有数据,那么就读取,否则函数会被阻塞,直到网络上有数据到来。
2) 如果要读取的数据长度小于缓冲区中的数据长度,那么就不能一次性将缓冲区中的所有数据读出,剩余数据将不断积压,直到有 recv() 函数再次读取。
3) 直到读取到数据后recv() 函数才会返回,否则就一直被阻塞。
这就是TCP套接字的阻塞模式。所谓阻塞,就是上一步动作没有完成,下一步动作将暂停,直到上一步动作完成后才能继续,以保持同步性。