网络中的进程是如何通信的?

在网络中进程之间进行通信的时候,那么每个通信的进程必须知道它要和哪个计算机上的哪个进程通信.否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的.其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程).这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互.

什么是套接字?

套接字是作为4BDS UNIX的进程通信机制,它用于描述 IP 地址和端口,是一个用于通信连接的文件描述符.举个例子来说.套接字就好像是银行的服务窗口.然后每个服务窗口上面都有一个标号(对应套接字的 IP 地址),然后银行又有规定标号为多少的窗口提供什么服务,标号为窗口提供什么服务(窗口标号就类似套接字中记录的 IP 地址,服务就类似着套接字的端口号),那么客户就可以根据这个窗口标号去请求所需要的服务.

在 socket 中是怎么做到对另一个套接字连接的

首先很明显,上面一直在强调两个进程在进行与网络通信的时候,两个进程都必须知道对方是谁(通过"ip地址+端口"),否则就无法进行通信,可以设想下你写了封信给你的朋友小明,那么你忘了写寄信地址和寄信人.那么你把这份信寄了.然后你就开始等待小明的回信,那么小明就算收到信了也不知道这封信是谁寄的和该把回复信寄给谁.那么你能收到回信么?

但是有时候你在写通信时候比如 tcp 的客户端,你发现你没有对套接字进行地址绑定也依旧可以和服务器通信,那这又是为什么?因为操作系统帮你做了这件事,操作系统在发现你没有将一个套接字绑定到对应的 ip 地址和端口号而调用 listen 和connect 的时候.那么它会自动给你分配 IP 地址和端口号.接着上面那个寄信的例子,假设你在寄信的时候是托给个朋友帮你寄.那么这个好心的朋友发现了你犯的这个粗心的错误.那么他便把在寄信人和寄信地址处填写上了你的名字和你的地址,那么小明就可以和你正常通信了.

既然这为什么服务器还要大费周章的去对一个监听端口进行地址绑定呢?我们还是用上面那个寄信的例子来说,假设你很有钱,有三套房子(即服务器对应三个 IP),你依旧没有写寄信人和寄信地址.那么你虽然托朋友给你寄,但是你这个朋友也不确定你现在住那套房子里,那么他便把这个三个地址之中的一个地址填了上去.而恰恰很不幸的是你已近很久没有住那套房子了.那么小明即使回复了你的信,你也可能收不到信了.所以服务器必须要对监听套接字进行地址进行绑定!

什么是大小端

因为计算机内存是以字节(八位)为单位来存储东西的.那么在存储大于一字节的数据时候就会存在一个问题,按什么样的顺序去存放这样的数据,是低位字节排放在内存的低地址端还是低位字节排放在内存的高地址端.然而这个往往和具体CPU架构有关,而非操作系统.那么首先我们来说明下大端模式和小端模式的区别:

    • 小端模式(Little-Endian):    就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端.(逻辑上的低低高高)
    • 大端模式(Big-Endian):      就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端.(像数据流一样填充)

这样也许不好理解那么举个例子来说.如果将一个 16 位的整数 0x1234 存放到一个短整型变量(short)中,这个短整型变量采用大端或者小端模式在内存中的存储由下表所示.

socket 中的 tcp 编程的大致流程

注意问题

  1. 大型数据的发送与接收.这里打大型数据指的是大于接收缓冲区或者发送缓冲区的数据.对于这种数据就需要多次调用 recv 或 send 等等的函数来发送或者接收.
  2. TCP 是基于流发送的.所以当间隔很短发送的数据,在服务器中可能因此一次性收到,对此要进行相关处理.
  3. 若 TCP 保持长连接的话,要进行检查连接是否存在.常见的方法是用心跳包来检查连接是否存在.
  4. 大小端问题.在网络传输数据的时候记得只要超过两字节的二进制数据就要用 htosXX 函数和 stohXX 函数来对数据进行处理,因为在终端平台的大小端问题.但是超过两字节的字符数据不需要注意终端平台大小端问题.在网络中 TCP/IP 各层协议将字节序(网络字节序)定义为Big-Endian
  5. 对于为绑定地址和端口的套接字系统会随机分配端口和绑定本机上的 IP 给套接字用.对于因特网域,如果指定IP地址为 INADDR_ANY,套接字端点可以被绑定到所有的系统网络接口.这意味着可以接受到这个系统锁安装的所有网卡的数据包,如果调用 connect 或 listen ,但没有绑定地址到一个套接字,系统会选一个地址并将其绑定到套接字.

地址结构体

IPv4:  为了使用不同格式地址能够被传入到套接字函数,地址被转换成通用的地址结构 sockaddr IPv4,在 IPv4 因特网域(AF_INET)中,套接字地址用如下结构 sockaddr_in 表示:
struct in_addr{in_addr_t s_addr;};
struct sockaddr_in{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
};
IPv6:  在 IPv6 因特网域(AF_INET6)套接字地址用如下结构 sockaddr_in6 表示:

struct in6_addr{
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};

地址查询

通过调用 gethostent 获取主机信息,当 gethostent 返回时,得到一个指向 hostent 结构体指针,该结构体可能包含一个静态的数据缓存区,每次调用 gethostlent 将会覆盖这个缓存区.返回的地址采用网络字节序.相关函数:

    • gethostent
    • sethostent
    • endhostent

相关结构体:

struct hostent{
char *h_name;
char **h_aliases;
char h_addrtype;
char h_length;
char **h_addr_list;
...
};

将协议名字和协议号采用以下函数映射

相关函数

    • getprotobyname
    • getprotobynumber
    • getprotoent
    • setprotoent
    • endprotoend

相关结构体

struct protoent{
char* p_name;
char** p_aliases;
int p_proto;

服务查询

服务是由地址的端口号部分表示的.每个服务由唯一的熟知的端口号来提供,采用函数 getservbyname 可以将一个服务名字映射到一个端口号,函数getservbyport将一个端口号映射到一个服务器名,或者采用函数 getservent顺序扫描服务数据库.相关函数:

    • getservbyname
    • getservbyport
    • getservent
    • setservent
    • endservent
相关结构体:

struct servent{
char *s_name;
char **s_aliases;
int s_port;
cjar *s_proto;
};

套接字的常用 API

  • 创建套接字:           socket()
    SOCK_RAW 套接字提供一个数据报接口用于直接访问下面的网络层(因特网域中为IP).使用这个接口时,应用程序负责构造自己的协议首部,这是因为传输协议(TCP和UDP等)被绕过了.当创建一个原始套接字时需要具有超级用户权限,用以防止恶意程序绕过内建安全机制来创造报文.
  • 对套接字进行端口和地址的绑定:  bind()
    bind一些限制

    • 在进程所运行的机器上,制定的地址必须有效,不能制定一个其他机器的地址
    • 地址必须和创建套接字时的地址族所支持的格式相匹配
    • 端口号必须不小与1024,除非该进程具有相应的特权
    • 一般只有套接字端点能够与地址绑定,尽管有些协议可以多重绑定


    假设我服务器绑定的套接字地址为 123.255.1.3 客户端只能从 123.255.1.3 地址建立请求连接请求

  • 监听套接字:           listen()
  • 连接套件子:           connect() 
    如果套接字描述符处于非阻塞模式下,那么在连接不能马上建立的时,connect 将会返回 -1,并且将 errno 设为特殊的错误码,EINPROGRESS . 应用程序可以使用 select 或 poll 来判断文件描述符何时可写,如果可写完成连接.函数 connect 还可以用于无连接的网络服务(SOCK_DGRAM).若在 SOCK_DGRAM 套接字上调用 connect,所有发送报文目的地址设字为 connect 调用中所制定的那个地址,这样每次传送报文时就不需要在提供地址,另外,仅接受来自指定地址的报文
  • 就收请求:            accept()
    函数 accept 锁返回的文件描述符是套接字描述符,该描述符连接到调用 connect 的客户端.这个新的套接字描述符和原始套接字(sockfd)具有相同的套接字类型和地址族.传给 accept 的原始套接字没有关联到这个连接,而是继续保持可用状态并接受其他连接请求.如果没有连接请求等待处理,accept会阻塞直到一个请求到来,如果sockfd处于非阻塞模式,accept会返回-1并将errno设置为 EAGAIN 或 EWOULDBLOCK
  • 数据通信:            
    • send() \ recv()
    • write() \ read()
    • recvfrom() \ sendto()
  • 关闭套接字:           close()
  • 解析主机名和地址:        gethostbyname()/gethostbyaddr()
  • 获得到接字地址getpeername 获得以连接的套接字连接着的对方地址
  • getsockname 获得套接字绑定的地址
  • 查询/设置套件子选项:         getsockopt()/setsockopt()
  • 资料

参考资料

liunx 套接字编程(Linux_C++)的更多相关文章

  1. Linux Socket 原始套接字编程

    对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...

  2. C++网络套接字编程TCP和UDP实例

    原文地址:C++网络套接字编程TCP和UDP实例作者:xiaojiangjiang 1.       创建一个简单的SOCKET编程流程如下 面向有连接的套接字编程 服务器: 1)  创建套接字(so ...

  3. Python黑帽编程2.8 套接字编程

    Python黑帽编程2.8 套接字编程 套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此.官方关于socket编程的 ...

  4. 探索UDP套接字编程

    UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务, ...

  5. 【Python网络编程】利用Python进行TCP、UDP套接字编程

    之前实现了Java版本的TCP和UDP套接字编程的例子,于是决定结合Python的学习做一个Python版本的套接字编程实验. 流程如下: 1.一台客户机从其标准输入(键盘)读入一行字符,并通过其套接 ...

  6. 基本套接字编程(7) -- udp篇

    1. UDP概述         UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互 ...

  7. 初探网络编程--TCP套接字编程演示

    今天看了一下<计算机网络:自顶向下方法>,也就是计算机网络的教材的应用层一章,决定实现以下后面的Java C/S应用程序的例子,用来演示TCP和UDP套接字编程. 程序流程如下: 1.一台 ...

  8. linux网络环境下socket套接字编程(UDP文件传输)

    今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...

  9. linux网络编程-(socket套接字编程UDP传输)

    今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...

随机推荐

  1. Spring JDBC 访问MSSQL

    在Spring中对底层的JDBC做了浅层的封装即JdbcTemplate,在访问数据库的DAO层完全可以使用JdbcTemplate完成任何数据访问的操作,接下来我们重点说说Spring JDBC对S ...

  2. Repeater 使用方法

    ItemTemplate: 包含要逐一呈现给数据源中的每个数据项的 HTML 元素和控件 AlternatingItemTemplate: 包含要逐一呈现给数据源中的其他每个数据项的 HTML 元素和 ...

  3. 2748: [HAOI2012]音量调节

    Description 一个吉他手准备参加一场演出.他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都要改变一次音量.在演出开始之前,他已经做好了一个列表,里面写着在每首歌开始之前他想要改 ...

  4. 如何制作u盘启动盘

    1,下载windows系统            许多人下载windows时会出现各种版本,我推荐在这里下载 你用上面的地址进行下载,一般用迅雷吧.下载结束你就有了镜像文件了. 2,下载安装碟软通 那 ...

  5. 关于在xml文件中的 error: invalid symbol: 'switch' 错误

    在xml布局文件中使用Switch控件时,出现error: invalid symbol: 'switch'报错,代码如下: <Switch android:id="@+id/swit ...

  6. iOS push过去的时候界面不能完全退出

    iOS push过去的时候界面不能完全退出 解决方法:设置self.view.backgroundcolor 1. initWithFrame方法是什么?  initWithFrame方法用来初始化并 ...

  7. 【转载】我也说 IEnumerable,ICollection,IList,List之间的区别

    做C#的同学们,都知道,一类只能有一个继承类,但可以实现多个接口.这句话就告诉我们:IEnumerable,ICollection,IList,List区别了 首先我看看 IEnumerable: / ...

  8. 个性二维码开源专题<前背景>

    //设置图片资源 private Image imgAgo; public override void SetParam() { base.SetParam(); // 读取前背景 string _i ...

  9. C# 数据类型与PostgreSQL 数据类型映射

    Npgsql 是基于ADO.NET 的PostgreSQL 数据驱动. Npgsql 官方 已经提供C# 数据类型与PostgreSQL数据类型的对应映射 地址: http://www.npgsql. ...

  10. 使用bower管理前端依赖

    bower,类似于npm.maven等后端管理构建工具一样,bower可以用来管理前端浏览器依赖,关于bower详细介绍参考官网:https://bower.io/ bower init命令:初始化项 ...