20190618——网络程序设计 socket

创建Socket

int s = socket(domain, type, protocol);
s: socket 描述符, 一个整数(打开文件表中的序号)
domain: 整数, 通信域
e.g., AF_INET (IPv4 协议) – 最常用
type: 通信类型
SOCK_STREAM: reliable, 2-way, connection-based service
SOCK_DGRAM: unreliable, connectionless,
protocol: 协议 – 通常设置为0,自动选择

bind函数

int status = bind(sockid, &localaddrport, size);
status: 错误状态, = -1 bind 错误
sockid: 整数, socket 描述符
localaddrport: struct sockaddr, 本地IP 地址和端口 (地址通常设置为 INADDR_ANY :服务员接受从所有网络接口来的连接请求)
size: addrport 结构的长度
bind之后socket拥有了四元组的一半(通信端点)

忽略bind的情况,就是客户端都是自动bind,服务器端无连接的需要手动bind

listen函数

Listen()通知协议进程准备接受socket上的连接请求
int status = listen(sock, queuelen);
status: 0 if listening, -1 if error 
sock: integer, socket 描述符
queuelen: integer, 等待连接的队列长度
listen 是非阻塞的: 将socket置为被动状态后立即返回

accept函数

int s = accept(sock, &name, &namelen);
s: integer, 新的 socket (用于数据传送)
sock: integer, 老 socket (监听socket)
name: struct sockaddr, 连接的另一方的地址
namelen: sizeof(name): 输入name的长度/返回返回的name的长度
输入参数,
输出参数
accept 是阻塞的: 在返回前一直在等待

connect函数

int status = connect(sock, &name, namelen);
status: 0 成功连接, -1 否则
sock: integer, socket to be used in connection
name: struct sockaddr: 被动方(服务员)地址
namelen: integer, sizeof(name)


TCP不保证接受端每次收到数据的长度与发送时相同
TCP可能将send的长数据分割为小段发送,也可能将许多短块积累成长段发送
TCP将发送数据看成字节流,没有块边界
具体发送时的分合取决于TCP的窗口
程序需要循环读取socket才能收到全部发送数据。

getsocketname getpeername 什么时候用,什么用处

作用都是获取本地的地址和端口号

getsockname很强,在bind之后就可以使用了。
getpeername必须要在建立连接之后才能使用

这是server的整个过程
在这里插入图片描述

有链接的udp
对于UDP socket同样也可使用connect调用连接服务器。此时得到有连接的UDP socket
对于有连接的socket,当客户只访问一个服务员时,不用再指定服务员的地址了。
无连接的UDP socket每次发送都要在sendto里指定服务员地址

select函数

int status = select(nfds, &readfds, &writefds, &exceptfds, &timeout);
status: 准备就绪的对象的数量, -1 错误
nfds: 最大的文件描述符+1
readfds: 检查是否可读的文件描述符集合
writefds:检查是否可写的文件描述符集合
exceptfds:要检查的注册了异常的文件描述符集合
timeout: select 从开始到返回的时间,即使没有就绪对象 ,0 or  
   (NULL for )

关于并发框架

循环迭代服务器程序。
用于简单的网络服务。
在当前顾客服务完成之前,新到达的顾客无法得到服务。
使用select调用在一个进程内同时服务多个顾客的TCP服务器程序
并发服务器程序,
为每个顾客派生一个子进程提供服务,这是Unix服务器程序通常的做法。
使用线程(轻量进程)替代进程实现并发服务器程序。
并发是对并行的模拟
在单处理器上模拟多进程并行运行
最终结果,并发与并行相同
fork()在子进程和父进程中返回不同的值
父进程:子进程的id
子进程: 0
据此进程可知自己是父进程还是子进程

execve函数

进程首先用fork生成自身的一个副本,然后用UNIX提供的execve系统调用将进程图像替换为其他程序,
但进程id不变。

信号机制

它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程
正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则
就执行默认的处理函数。

用我的话来说,就是给进程提示了一个信号,进程正常的控制流程都被终端,任何非原子操作都会被终端,如果
进程定义了信号处理函数那么就会执行,若非那么就执行默认的处理函数

慢系统调用

线程阻塞通常是指一个线程在执行过程中暂停,以等待某个条件的触发
能够永久阻塞的系统调用,称为慢系统调用slow,永远阻塞的系统调用是指调用永远无法返回,
多数网络支持函数都属于这一类
当进程捕获到中断,中断处理结束时,某些系统中慢系统调用将返回错误EINTER,某些则自动继续执行系统调用。

wait和waitpid

返回已经终止的子进程的进程ID号,并清除僵死进程。
wait是阻塞的
waitpid非阻塞
多个相同信号同时出现时。
Unix不对信号排队
导致使用wait将只调用一次信号处理函数
waitpid可不阻塞,因此通过查询每个子进程的状态,得到终止进程的pid,进行善后处理。

inetd

inetd是监视一些网络请求的守护进程,其根据网络请求来调用相应的服务进程来处理连接请求。它可以为多种服务管理连接,当 inetd 接到连接时,它能够确定连接所需的程序,启动相应的进程,并把 socket 交给它 (服务 socket 会作为程序的标准输入、 输出和错误输出描述符)。 使用 inetd 来运行那些负载不重的服务有助于降低系统负载,因为它不需要为每个服务都启动独立的服务程序。

在这里插入图片描述

Inetd工作过程如下:

1.inetd 启动时,从配置文件/etc/inetd.conf中读入一行,取得服务名及其相关信息,并根据服务所指定使用的协议调用socket()函数生成相应的套接字;

2.从文件/etc/services查找服务所使用的端口号,调用函数bind()将套接字绑定到所获得的端口;

3.若服务使用TCP协议,调用listen()函数,否则直接跳到步骤4;

4.是否为文件/etc/inetd.conf中所定义的服务都生成了套接字,若没有转步骤1;否则往下执行步骤5;

5.调用select()监听,一旦TCP类型的套接字收到连接请求或是UDP类型的套接字有数据到达,select()函数返回;对于TCP套接字,函数accept()返回一个新的套接字和客户端建立连接,而UDP类型的套接字则直接使用一开始生成的套接字;

6.调用fork()函数生成子进程;

7.若为服务使用TCP协议,其服务标志是“nowait”,父进程在关闭连接套接字后直接返回步骤5继续监听;若使用UDP 协议,则其服务标志为“wait”,父进程需要等待子进程结束后才能返回。在父进程运行的同时,子进程生成后关闭除连接套接字外所有的文件;

8.子进程调用三次dup2()函数分别把套接字复制到文件描述符为0(标准输入)、1(标准输出)、2(标准错误)的文件,然后关闭套接字。这样,子进程只打开三个文件:0、1、2;

9.若用户名不是root,需设置用户组号和用户号,使子进程在一定的用户权限下运行;

10.调用exec()函数用相应的服务程序取代子进程,这样服务程序继承了子进程的运行环境。

上述步骤中,1~4完成inetd的初始化和设置,5实现监听,6~9完成实际服务进程启动前的准备工作,10在准备好的运行环境中启动实际服务进程。

dup dup to

创建一个oldfd的副本,最小的未使用文件描述符
成功调用后,新旧文件描述符可通用
共享文件指针、文件状态

函数dup和dup2提供了复制文件描述符的功能。
与dup功能相同,但副本文件描述符是newfd

子进程如何得到客户的地址

子进程如何得到客户的地址?
描述符0,1,2都对应socket,可通过getpeername 得到
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值