套接字api-基本TCP套接字编程

socket

int socket(
	// AF_INET/AF_INET6/AF_LOCAL
	int family, 
	// SOCK_STREAM/SOCK_DGRAM/SOCK_RAW
	int type, 
	// IPPROTO_TCP/IPPROTO_UDP
	int protocol);

connect

int connect(
	int sockfd,
	const struct sockaddr* servaddr,
	socklen_t addrlen);

客户发出SYN,可能的失败情形:
a. SYN未到达服务端,最终触发超时.
b. 服务端对应端口并未有监听进程,服务端主机发回RST
失败时,必须对sockfd执行close

bind

int bind(
	int sockfd, 
	const struct sockaddr* myaddr, 
	socklen_t addrlen);

1.一个主机有多个网络接口,每个网络接口有一个IP地址
TCP客户,执行bind指定IP地址,则为后续sockfd上发出的IP数据报限定了源IP地址
TCP客户,执行bind不指定IP地址,则为后续sockfd上发出的IP数据报的源IP地址采取发出的网络接口的IP地址

TCP服务器,执行bind指定IP地址,则限定后续sockfd监听时只接收目的IP为这里指定IP地址的连接请求
TCP服务器,执行bind不指定IP地址,则限定后续sockfd监听时接收目的IP为主机上任意网络接口IP地址的连接请求.且采用客户发来SYN的目的IP地址作为服务器上对应已经连接套接字的本端IP地址.

2.IP地址为INADDR_ANY表不指定,端口为0表不指定,端口未指定时,由内核为此sockfd指定
3.主机可收取IP地址为其任一网络接口IP地址的数据报,数据报递送到进程,则需要按数据报的(源IP地址,源端口,目的IP地址,目的端口)递送到本机中与其匹配的套接字的缓冲区.随后,可通过匹配套接字调read读入该套接字缓冲区的数据到进程.

listen

int listen(int sockfd, int backlog);

内核为任何一个给定的监听套接字维护两个队列
1.未完成连接队列
收到SYN,尚未完成三路握手的连接.一般在未完成连接队列已经满,接着收到SYN,后续收到的SYN将忽略(对端会超时重传).
2.已完成连接队列
已经完成三路握手.

已完成连接队列中套接字关联的数据到达主机,即使尚未accept,该套接字的数据也会放入该套接字的接收缓冲区.待其后续读取.

accept

int accept(
	int sockfd,// 监听套接字描述符
	// 返回已连接对端进程的协议地址 
	struct sockaddr* cliaddr, 
	socklen_t* addrlen);// 值-结果参数

内核为每个服务器进程接受的客户创建一个已连接套接字

close

int close(int sockfd);

每个文件或套接字有一个引用计数,在文件表项中维护.代表引用此文件或套接字的描述符的个数.
fork造成父进程打开的描述符被复制到子进程,相应的描述符指向文件或套接字的引用计数+1
对描述符执行close时,先将其指向的文件或套接字引用计数-1,若变为0,执行清理和释放工作(包含向对端发FIN).否则,更新引用计数后结束.

套接字引用计数变为0后,以其为目的地址的write,将收到RST.此RST到达对端后,对端再对其执行write时,应用层将收到通知.
引用计数变为0,也会引发向对端发FINFIN到达对端后,对端发回FIN ACK,此后对端再对其执行write,应用层也将收到通知.此后对端仍可对其执行read,直到读完其发送缓冲区,再读时,应用层将收到通知.

以其为源地址的read,该套接字发送缓冲区的数据由内核负责继续发给对端.发送完毕后,对端再对其执行read,应用层将收到通知.该套接字接收缓冲区的数据被直接丢弃.(因为后续也无法在取出尚在缓冲区的数据了)

getsockname和getpeername

// 获取套接字[源IP地址,源端口]
int getsockname(
	int sockfd, 
	struct sockaddr*,
	socklen_t*);// 值-结果参数
// 获取套接字[目的IP地址,目的端口]
int getpeername(
	int sockfd, 
	struct sockaddr*,
	socklen_t*);// 值-结果参数

信号中断

accept, read, write, select, open等被信号中断后,应该处理为再次重启
connect被信号中断后,不可再次connect,应select,等待connect关联的套接字可写,可读(收到对端SYN ACK后,可写,可读)

并发式服务端注意点

1.对fork的子进程,需捕获其终止所产生的SIGCHLD,避免产生僵尸进程
2.需要处理被中断的系统调用(再次执行/connect下的调select等待)
3.SIGCHLD处理函数需要回收所有子进程(防止处理期间产生多次SIGCHLD,未排队,到导致有的SIGCHLD被忽略,进而无法回收其对应的子进程的情况)

SIGCHLD处理及其正确性说明

信号语义:
1.一次注册,持续有效
2.信号处理期间,对应信号被屏蔽.且可指定其他屏蔽信号.处理结束恢复信号屏蔽.
3.系统调用使用时需考虑到对信号中断处理
4.信号不排队.阻塞期间产生多次同类信号,只记录一次.该信号不阻塞时,提交一次.

void sig_chld(ing signo)
{
	xxx
	while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
	{
		...	
	}
	xxx
}

SIGCHLD处理的原因是,若不处理,则因终止而发送SIGCHLD的子进程,在被init回收前将成为僵尸进程.占据系统资源.
SIGCHLD的处理要求,不能遗漏调任何发送SIGCHLDL的子进程.
而信号的特点有,在SIGCHLD处理过程中,到达的一个或多个SIGCHLD不会立即再次触发信号处理,只会在待处理信号集合先记录.待SIGCHLD处理过程结束,若待处理信号集合仍有SIGCHLD,则再次触发处理过程.
在非SIGCHLD处理过程中,到达一个SIGCHLD,会中断进程现有处理转而执行SIGCHLD处理过程.

所有终止的进程及其相关信息全部记录在内核中,这些信息可以通过waitpid来获取,并由此让内核释放这些信息.上述处理中,每次处理过程,我们对当前已知的所有终止子进程完成一次处理并结束.
即使,上述while后的语句执行中,处理函数尚未返回前,又产生了多个子进程终止,这些子进程无法被本次信号处理所回收,且由于信号的特点,在信号处理函数结束后,待处理信号集合SIGCHLD被设置,再次触发一次SIGCHLD信号处理,但由于信号处理while的特点,在本次处理中我们仍然可以回收完全上次未被回收的一个或多个子进程,故整个过程下来,可以保证所有子进程均能得到回收,不会遗漏.
另一个地方是,由于waitpid指定WNOHANG,故在没有子进程终止时,可以让回收处理快速结束,不会无意义的阻塞在那里.

TCP套接字归纳

1.内核接收到发往本机的套接字数据
若本机有对应的TCP连接套接字可接收数据,将到来的数据放入对应套接字的接收缓冲区
若本机没有对应的TCP连接套接字可接收数据,发回RST响应消息
对接收到的SYN请求,会交给监听套接字处理,处理结果是产生一个已连接套接字,并发回SYN ACK & SYN

2.进程使用套接字进行read
若此套接字接收缓冲区有数据可读,读取不超过缓冲区大小和可读数据量的数据
若此套接字接收缓冲区无数据可读,若已经收到对端的FIN,则read返回0
若已经收到对端的FIN,且收到对端的RST(对端异常终止或采用close而非shut执行关闭,对端上内核中的不再维护相应套接字信息),read返回错误(ECONNRESET
若未收到对端的FIN,则read阻塞等待,若在read阻塞等待的过程中,由于前面内核从套接字发送缓冲区取数据写往对端时,由于对端主机关机或网络中断造成重传并最终超时时,内核会引发异步通知(前面write无法写的到对端,内核把此信息在后续调read时,反馈给应用),进程read阻塞会被中断,并返回相应错误码ETIMEDOUT/EHOSTUNREACH/ENETUNREACH

3.进程使用套接字write
若发送缓冲区可写,写入数据量不超过套接字发送缓冲区可写数据量与要写数据量
若发送缓冲区已满,阻塞等待或返回错误
若进程已经收到对端的RST,则write会返回错误EPIPE并产生SIGPIPE

TCP网络通信数据传递

不同主机上对同类型数据的位数,存储方式不同,在跨越主机传递数据时.
若参与通信各个主机有相同字符集,传递信息统一采取字符串方式,接收后,可以按需求进行解析处理.
若向直接以二进制格式传递(数据对象首字节指针,数据对象大小方式),参与通信各方需针对数据的位数,存储方式达成一致.

缓冲区

TCP拥有数据接收和发送缓冲区.
本机的read/write与套接字关联的内核缓冲区交互.
readUDP内核先完成一个完整的UDP数据报的接收,然后即会将接收到数据报发给应用.
writeUDP数据报先完整写入内核,然后立即由内核将其发出.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值