UNIX再学习 -- 网络IPC:套接字

一、基本概念

1、编程接口

什么是伯克利套接字(Berkeley Socket)?

美国加利福尼亚大学比克利分校于 1983年发布 4.2 BSD Unix 系统。其中包含一套用 C 语言编写的应用程序开发库。该库既可用于在同一台计算机上实现进程间通信,也可用于在不同计算机上实现网络通信。当时的 Unix 还受 AT&T 的专利保护,因此直到 1989 年,伯克利大学才能自由发布他们的操作系统和网络库,而后者即被称为伯克利套接字应用编程接口(Berkeley Socker APIs)。
伯克利套接字接口的实现完全基于 TCP/IP 协议,因此它是构建一切互联网应用的基石。几乎所有现代操作系统都或多或少有一些源自伯克利套接字接口的实现。它已成为应用程序连接互联网的标准接口。

什么是套接字?

套接字(socket)的本意是指电源插座,这里将其引申为一个基于 TPC/IP 协议可实现基本网络通信功能的逻辑对象。
机器与机器的通信,或者进程与进程的通信,在这里都可以被抽象地看作是套接字与套接字的通信。
应用程序编写者无需了解网络协议的任何细节,更无需知晓系统内核和网络设备的运作机制,只要把想发送的数据写入套接字,或从套接字中读取想接收的数据即可。
从这个意义上讲,套接字就相当于一个文件描述符,而网络就是一种特殊的文件,面向网络的编程与面向文件的编程已没有分别,而这恰恰是 unix 系统一切皆文件思想的又一例证。

什么是套接字的异构性?

如前所述,套接字是对 ISO/OSI 网络协议模型中传输及其以下诸层的逻辑抽象,是对 TCP/IP 网络通信协议的高级封装,因此无论所依赖的是什么硬件,所运行的什么操作系统所使用的是什么编程语言,只要是基于套接字构建的应用程序,只要是在互联网环境中通信,就不会存在任何障碍。

2、通信模式

单播模式

每个数据包发送单个目的主机,目的地址指明单个接收者。
服务器可以及时响应客户机的请求。
服务器可以针对不同客户的不同请求提供个性化的服务。
网络中传输的信息量与请求该信息的用户量成正比,当请求该信息的用户量较大时,网络中将出现多份内容相同的信息流,此时带宽就成了限制传输质量的瓶颈。

广播模式

一台主机向网上的所有其它主机发送数据。
无需路径选择,设备简单,维护方便,成本低廉。
服务器不用向每个客户机单独发送数据,流量负载极低。
无法针对具体客户的具体要求,及时提供个性化的服务。
网络无条件地复制和转发每一台主机产生的信息,所有的主机可以收到所有的信息,而不管是否需要,网络资源利用率低,带宽浪费严重。
禁止广播包穿越路由器,防止在更大范围内泛滥。

多播模式

网络中的主机可以向路由器请求加入或退出某个组,路由器和交换机有选择地复制和转发数据,只将组内数据转发给那些加入组的主机。
需要相同信息的客户机只要加入同一个组即可共享同一份数据,降低了服务器和网络的流量负载。
既能一次将数据传输给多个有需要的主机,又能保证不影响其他不需要的主机。
多播包可以穿越路由器,并在穿越中逐渐衰减。
缺乏纠错机制,丢包错包在所难免。

3、绑定与连接

如前所述,套接字是一个提供给程序员使用的逻辑对象,它表示对 ISO/OSI 网络协议模型中传输层及其以下诸层的的抽象。但真正发送和接收数据的毕竟是那些实实在在的物理设备。这就需要在物理设备和逻辑对象之间建立一种关联,使后续所有针对这个逻辑对象的操作,最终都能反映到实际的物理设备上。建立这种关联关系的过程就叫做绑定

绑定只是把套接字对象和一个代表自己的物理设备关联起来。但为了实现通信还需要把自己的物理设备与对方的物理设备关联起来。只有这样才能建立一种以物理设备为媒介的,跨越不同进程甚至机器的,多个套接字对象之间的联系。建立这种联系的过程就叫做连接

二、常用函数

1、函数 socket:创建套接字

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
返回值:成功返回套接字描述符,失败返回 -1

(1)参数解析

domain:通信域,即地址族,可取以下值:
AF_LOCAL/AF_UNIX 本地通信,即进程间通信
AF_INET 基于 IPv4 的网络通信
AF_INET6 基于 IPv6 的网络通信
AF_PACKET 基于底层包接口的网络通信
tyoe:套接字类型,可取以下值:
SOCK_STREAM 流式套接字,即使用 TCP 协议的套接字。有序的、可靠的、双向的、面向连接的字节流。
SOCK_DGRAM 数据报套接字,即使用 UDP 协议的套接字。固定长度的、无连接的、不可靠的报文传递。
SOCK_RAW 原始套接字,即使用 IP 协议的套接字。
SOCK_SEQPACKET 固定长度的、有序的、可靠的、面向连接的报文传递。
protocol:特殊协议,通常不用,取 0 即可。表示为给定的域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用 protocol 选择一个特定协议。

(2)函数解析

socket 函数所返回的套接字描述符类似于文件描述符,Unix 系统把网络也看成是文件,发送数据即写文件,接收数据即读文件,一切皆文件。
虽然套接字描述符本质上是一个文件描述符,但不是所有参数为文件描述符的函数都可以接受套接字描述符。
下图总结了到目前为止所讨论的大多数以文件描述符为参数的函数使用套接字描述符的行为。未指定和由实现定义的行为通常意味着该函数对套接字描述符无效。例如,lseek 不能以套接字描述符为参数,因为套接字不支持文件偏移量的概念。

2、函数 shutdown:禁止套接字

套接字通信是双向的。可以采用 shutdown 函数来禁止一个套接字的 I/O。
#include<sys/socket.h>  
int shutdown(int sockfd,int how); 
返回值:若成功则返回0,出错则返回-1.  
如果 how 是 SHUT_RD (关闭读端),那么无法从套接字读取数据;如果 how 是 SHUT_WR (关闭写端),那么无法使用套接字发送数据。如果 how 是 SHUT_RDWR ,则既无法读取数据,又无法发送数据。
能够关闭(close)套接字,为什么还要使用 shutdown 呢?这里有若干个理由。
首先,只有最后一个活动引用关闭时,close 才释放网络端点。这意味着,如果复制一个套接字(如采用 dup),套接字直到关闭了最后一个引用它的文件描述符之后才会被释放。而 shutdown 允许使一个套接字处于不活动状态,和引用它的文件描述符数目无关。
其次,有时可以很方便地关闭套接字双向传输中的一个方向。

3、地址结构

套接字接口库通过地址结构定位一个通信主体,可以是一个文件,可以是一台远程主机,也可以是执行自己。 基本地址结构,本身没有实际意义,仅用于泛型化参数。
为使不同格式地址能够传入到套接字函数,地址会被强制转换成一个通用的地址结构 sockaddr
struct sockaddr{
sa_family_t sa_family; //地址族
char sa_data[]; //地址值
...
};

本地地址结构,用于 AF_LOCAL/AF_UNIX 域的本地通信

#include <sys/un.h>
struct sockaddr_un
{
	sa_family_t sun_family; //地址族(AF)LOCAL)
	char sun_path[];  //踏破戒指文件路径
};

网络地址结构,用于 AF_INET 域的 IPv4 网络通信

#include <netinet/in.h>
struct sockaddr_in
{
	sa_family_t sin_family;  //地址族(AF_INET)
	in_port_t sin_port;  //端口号
	struct in_addr sin_addr;  //IP地址
};

struct in_addr
{
	in_addr_t s_addr;
};

typedef unit16_t in_port_t;  //无符号短整型
typedef unit32_t in_addr_t;  //无符号长整型
如前所述,通过 IP 地址可以定位网络上的一台主机,但一台主机上可能同时又多个网络应用在运行,究竟想跟哪个网络应用通信呢?这就需要靠所谓的端口号来区分,因为不同的网络应用会使用不同的端口号。用 IP 地址定位主机,再用端口号定位运行子啊这台主机上的一个具体的网络应用,这样一种对通信主体的描述才是唯一确定的。
套接字接口库中的端口号被定义为一个 16 位的无符号整数,其值介于 0 到 65535,其中 0 到 1024 已被系统和一些网络服务占据,比如 21 端口用于 ftp 服务、23端口用于 telnet服务。80端口用于 www 服务等,因此一般应用程序最好选择 1024 以上的端口号,以避免和这些服务冲突
网络应用与单机应用不同,经常需要在具有不同硬件架构和操作系统的计算机之间交换数据,因此编程语言里一些多字节数据类型的字节序问题就是需要特别予以关注。
这就涉及到大小端问题,参看:C语言再学习-- 大端小端详解(转)
假设一台小端机器里有一个 32 位整数:0x1234 5678,它在内存中按照小端字节序低位低地址的规则存放:

现在,这个整数通过网络被传送到一台大端机器里,内存中的形态不会有丝毫差别,但在大端机器看来地址越低的字节数位应该越高,因此它会把这 4 个字节解读为:0x7856 3412,而这显然有悖于发送端的初衷。
为了避免字节序带来的麻烦,套接字接口库规定凡是在网络中交换的多字节整数(short、int、long、long long 和它们的 unsigned 版本)一律采用网络字节序传输。
而所谓网络字节序,其实就是大端字节序。也就是说,发数据时,先从主机字节序转成网络字节序,然后发送;收数据时,先从网络字节序转成主机字节序,然后使用。

网络地址结构 sockaddr_in 中表示端口号的 sin_port 成员和表示 IP 地址的 sin_addr.s_addr 成员,分别为 2 字节和 4 字节的无符号整数,同样需要用网络字节序来表示。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把2280端口映射到公网,发现经常被暴力破解,自己写了个临时封禁ip功能的脚本,实现5分钟内同一个ip登录密码错误10次就封禁这个ip5分钟,并且进行邮件通知使用步骤openwrt为19.07.03版本,其他版本没有测试过安装bashmsmtpopkg
#!/bin/bashcommand1&command2&wait从Shell脚本并行运行多个程序–杨河老李(kviccn.github.io)
1.先查出MAMP下面集成的PHP版本cd/Applications/MAMP/bin/phpls-ls 2.编辑修改.bash_profile文件(没有.bash_profile文件的情况下回自动创建)sudovim~/.bash_profile在文件的最后输入以下信息,然后保存退出exportPATH="/Applications/MAMP/bin/php/php7.2.20/b
1、先输入locale-a,查看一下现在已安装的语言2、若不存在如zh_CN之类的语言包,进行中文语言包装:apt-getinstalllanguage-pack-zh-hans3、安装好后我们可以进行临时修改:然后添加中文支持: locale-genzh_CN.UTF-8临时修改> export LC_ALL='zh_CN.utf8'> locale永久
BashPerlTclsyntaxdiff1.进制数表示Languagebinaryoctalhexadecimalbash2#[0~1]0[0~7]0x[0~f]or0X[0~f]perl0b[0~1]0[0~7]0x[0~f]tcl0b[0~1]0o[0~7]0x[0~f]bashdifferentbaserepresntationreference2.StringlengthLanguageStr
正常安装了k8s后,使用kubect工具后接的命令不能直接tab补全命令补全方法:yum-yinstallbash-completionsource/usr/share/bash-completion/bash_completionsource<(kubectlcompletionbash)echo"source<(kubectlcompletionbash)">>~/.bashrc 
参考这里启动jar包shell脚本修改过来的#!/bin/bash#默认应用名称defaultAppName='./gadmin'appName=''if[[$1&&$1!=0]]thenappName=$1elseappName=$defaultAppNamefiecho">>>>>>本次重启的应用:$appName<
#一个数字的行#!/bin/bashwhilereadlinedon=`echo$line|sed's/[^0-9]//g'|wc-L`if[$n-eq1]thenecho$linefidone<1.txt#日志切割归档#!/bin/bashcd/data/logslog=1.logmv_log(){[-f$1]&&mv$1$2
#文件增加内容#!/bin/bashn=0cat1.txt|whilereadlinedon=[$n+1]if[$n-eq5]thenecho$lineecho-e"#Thisisatestfile.\n#Testinsertlineintothisfile."elseecho$linefidone#备份/etc目录#
# su - oraclesu: /usr/bin/ksh: No such file or directory根据报错信息:显示无法找到文件 /usr/bin/ksh果然没有该文件,但是发现存在文件/bin/ksh,于是创建了一个软连接,可以规避问题,可以成功切换到用户下,但无法执行系统自带命令。$. .bash_profile-ksh: .: .b
history显示历史指令记录内容,下达历史纪录中的指令主要的使用方法如果你想禁用history,可以将HISTSIZE设置为0:#exportHISTSIZE=0使用HISTIGNORE忽略历史中的特定命令下面的例子,将忽略pwd、ls、ls-ltr等命令:#exportHISTIGNORE=”pwd:ls:ls-ltr:”使用HIS
一.命令历史  1.history环境变量:    HISTSIZE:输出的命令历史条数,如history的记录数    HISTFILESIZE:~/.bash_history保存的命令历史记录数    HISTFILLE:历史记录的文件路径    HISTCONTROL:     ignorespace:忽略以空格开头的命令
之前在网上看到很多师傅们总结的linux反弹shell的一些方法,为了更熟练的去运用这些技术,于是自己花精力查了很多资料去理解这些命令的含义,将研究的成果记录在这里,所谓的反弹shell,指的是我们在自己的机器上开启监听,然后在被攻击者的机器上发送连接请求去连接我们的机器,将被攻击者的she
BashOne-LinersExplained,PartI:Workingwithfileshttps://catonmat.net/bash-one-liners-explained-part-oneBashOne-LinersExplained,PartII:Workingwithstringshttps://catonmat.net/bash-one-liners-explained-part-twoBashOne-LinersExplained,PartII
Shell中变量的作用域:在当前Shell会话中使用,全局变量。在函数内部使用,局部变量。可以在其他Shell会话中使用,环境变量。局部变量:默认情况下函数内的变量也是全局变量#!/bin/bashfunctionfunc(){a=99}funcecho$a输出>>99为了让全局变量变成局部变量
1、多命令顺序执行;  命令1;命令2  多个命令顺序执行,命令之间没有任何逻辑联系&&  命令1&&命令2  逻辑与,当命令1正确执行,才会执行命令2||  命令1||命令2  逻辑或,当命令1执行不正确,才会执行命令2例如:ls;date;cd/home/lsx;pwd;who ddif=输入文件of=输
原博文使用Linux或者unix系统的同学可能都对#!这个符号并不陌生,但是你真的了解它吗?首先,这个符号(#!)的名称,叫做"Shebang"或者"Sha-bang"。Linux执行文件时发现这个格式,会把!后的内容提取出来拼接在脚本文件或路径之前,当作实际执行的命令。 Shebang这个符号通常在Unix系统的脚本
1、历史命令history[选项][历史命令保存文件]选项:-c:  清空历史命令-w:  把缓存中的历史命令写入历史命令保存文件 ~/.bash_historyvim/etc/profile中的Histsize可改存储历史命令数量历史命令的调用使用上、下箭头调用以前的历史命令使用“!n”重复执行第n条历史
目录1.Shell脚本规范2.Shell脚本执行3.Shell脚本变量3.1环境变量3.1.1自定义环境变量3.1.2显示与取消环境变量3.1.3环境变量初始化与对应文件的生效顺序3.2普通变量3.2.1定义本地变量3.2.2shell调用变量3.2.3grep调用变量3.2.4awk调用变量3.3
   http://www.voidcn.com/blog/wszzdanm/article/p-6145895.html命令功能:显示登录用户的信息命令格式:常用选项:举例:w显示已经登录的用户及正在进行的操作[root@localhost~]#w 11:22:01up4days,21:22, 3users, loadaverage:0.00,0.00,0.00USER