zoukankan      html  css  js  c++  java
  • socket套接字原理学习

    转自:

    https://blog.csdn.net/pashanhu6402/article/details/96428887

    https://blog.csdn.net/noricky/article/details/82626188

    http://www.360doc.com/content/20/0218/14/99071_892916049.shtml

    1.网络进程中的通信

     同一主机上,不同进程可用进程号(process ID)唯一标识,通过管道、命名管道、消息队列、共享内存、信号量等方式进行通信,那么网络中的不同主机如何通信?

     网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

    2.socket所在层次

     负责在用户进程和TCP运输层之间通信,相当于封装了TCP/UDP为库函数,用户直接调用就可以。

    即它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面。

    3.套接字描述符

    • 套接字描述符也就是一个文件描述符,就是一个整数;我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr。
    • 当应用程序要为因特网通信而创建一个套接字(socket)时,操作系统就返回一个小整数作为描述符(descriptor)来标识这个套接字。
    • 然后,应用程序以该描述符作为传递参数,通过调用函数来完成某种操作(例如通过网络传送数据或接收输入的数据)。

    在许多操作系统中,套接字描述符和其他I/O描述符是集成在一起的,所以应用程序可以对文件进行套接字I/O或I/O读/写操作:

     每个进程PCB中都保存指向文件描述符的指针,文件描述符中存储着进程打开的文件,每个文件描述符都和一个文件绑定,可以通过函数进行IO操作。

    • 精确地讲,系统为每个运行的进程维护一张单独的文件描述符表。
    • 当进程打开一个文件时,系统把一个指向此文件内部数据结构的指针写入文件描述符表,并把该表的索引值返回给调用者 。
    • 应用程序只需记住这个描述符,并在以后操作该文件时使用它。
    • 操作系统把该描述符作为索引访问进程描述符表,通过指针找到保存该文件所有的信息的数据结构。

    套接字数据结构:

     套接字API里有个函数socket,它就是用来创建一个套接字。

    文件指针是个结构体类型,其中包含文件描述符,文件描述符作为索引,可以在文件描述符表中找到指向文件的指针。

    4.socket通信过程

    socket():

    通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器,客户端通过这个端口填入connect()函数形参;

    而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

    通过Socket建立通信连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket。Socket原意是 “插座”,两个Socket端点的连接,就像连接一个插座获取服务一样。

    accept():

    accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。

    此时我们需要区分两种套接字,

    •  监听套接字: 监听套接字正如accept的参数sockfd,它是监听套接字,在调用listen函数之后,是服务器开始调用socket()函数生成的,称为监听socket描述字(监听套接字)
    •  连接套接字:一个套接字会从主动连接的套接字变身为一个监听套接字;而accept函数返回的是已连接socket描述字(一个连接套接字),它代表着一个网络已经存在的点点连接。

    两种不同的socket,分工不同。

    连接套接字socketfd_new 并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd一样的端口号。

    可以通过Read()/write()等函数从连接套接字中读写数据。那么socket是如何实现数据在两个主机之间发送的呢?

     

    Linux内核组成图

    由上述两张图片可以看出,read/write()等函数也是封装了更底层的协议驱动程序,当写入socket文件时,在某个时机通过在TCP/IP协议驱动程序封装,再在以太网卡驱动程序中封装为以太网帧,然后通过硬件网线或者无线网络电磁波发送,具体的数据传输肯定都是以比特流在物理层传输的,socket只是负责上层的工作,具体的传输是交给其他层去完成,我们只负责调用函数就行了。

    ///具体:

    Socket在应用程序中创建,通过绑定与网络驱动建立关系。此后,应用程序送给Socket的数据,由Socket交给网络驱动程序向网络上发送出去。计算机从网络上收到与该Socket绑定IP地址和端口号相关的数据后,由网络驱动程序交给Socket,应用程序便可从该Socket中提取接收到的数据,网络应用程序就是这样通过Socket进行数据的发送与接收的。
    通过图片对文字的可视化,黄色表示网络驱动,通过网卡发送与接收数据。
    作者:水止云起
    链接:https://www.jianshu.com/p/8c1f37361a89
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    那么什么时候文件描述符是可读的、可写的?

    (可读,已经通过以太网接收到了数据,上层应用程序读;可写,上层应用程序想发送数据给另一方,socket文件描述符是否能写?写好了就通过以太网传给对方。)

     

    通过上面可以发现,socket有内核接收缓冲区、发送缓冲区,并不都是直接写到文件里的。那么有个疑问,接收缓冲区的内容有没有写入过socket中?

    见下一篇博客。

  • 相关阅读:
    oracle改表语句
    pr视频过渡效果
    远程桌面连接
    kill-power
    Leetcode 466.统计重复个数
    Leetcode 464.我能赢吗
    Leetcode 462.最少移动次数使数组元素相等
    Leetcode 459.重复的子字符串
    Leetcode 458.可怜的小猪
    Leetcode 457.环形数组循环
  • 原文地址:https://www.cnblogs.com/BlueBlueSea/p/14373849.html
Copyright © 2011-2022 走看看