socket学习
socket(…)
1 |
|
family : 协议族,包括IPv4, IPv6…
family | 说明 |
---|---|
AF_INET | IPv4协议 |
AF_INET6 | IPv6协议 |
AF_LOCAL | Unix域协议 |
AF_ROUTE | 路由套接字 |
AF_KEY | 密钥套接字 |
type : 套接字类型,包括SOCK_STREAM(字节流), SOCK_DGRAM(数据报)….
type | 说明 |
---|---|
SOCK_STREAM | 字节流套接字 |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 有序分组套接字 |
SOCK_RAW | 原始套接字 |
protocol : 某个协议类型常值,设置为0以选择给定family和type组合的系统默认值,包括TCP,UPD,SCTP
protocol | 说明 |
---|---|
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_STCP | STCP传输协议 |
connect(…)
1 |
|
sockfd:socket返回的套接字描述符
2,3是指向一个套接字结构的指针和结构的大小
值得注意的是,connect函数会导致当前套接字从CLOSED状态转移到SYS_SENT状态
若成功,则转移到established(已建立)状态
若失败,则该套接字不可用,必须关闭
我们不能对这样的套接字再次调用connect函数,必须close然后重新调用socket
bind(…)
1 |
|
bind(…)的用处在于把一个协议地址赋予一个套接字
在服务器上,bind要绑定一个众所周知的端口
而在客户端上,这个可以使用内核分配的临时端口
listen()
1 |
|
当socket函数创建一个套接字的时候,它就被设置为一个将调用connect的客户套接字
而listen会将其转换成一个被动套接字,从而接受指向该套接字的连接请求
调用listen后,套接字会从close转换到listen状态
backlog:内核应该为相应套接字排队的最大连接数
我们必须认识到,内核为每一个给定的监听套接字维护两个队列
未完成连接队列 : 每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手
已完成连接队列 : 每个已完成三路握手的客户端对应其中一项
当客户的SYN到达时,TCP就会在未完成连接队列中创建一个新项,然后相应三路握手的第二个分节
然后该项就会在队列中一直保留,直到三路握手的第三个分节到达或者超时为止,
此时服务器进入SYN_RECV状态,当服务器未收到客户端的ACK时,重发请求包,一直到超时,才将此条目从未连接队列删除
SYN攻击就是利用这个特性,通过发送大量的半连接请求,耗费CPU和内存资源
这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
accept()
1 |
|
accept由TCP服务器调用,如果已完成连接队列为空,那么进程将被投入睡眠
如果accept成功,返回值则是一个全新的描述符,代表与客户的TCP连接
EINTR(系统中断)
在处理僵尸进程的时候,我们曾经为处理SIGCHLD信号调用了wait/waitpid
函数
这时候,系统为了处理这个信号产生了一次中断,这会使得accept产生一个EINTR错误
有些内核可以自动重启某些被中断的系统调用,但是为了保证移植性(因为某些内核不支持重启),我们必须对返回的ENITR有所处理
1 | while (1) { |
close()
1 |
|
将套接字标记成已关闭,然后立即返回到调用进程
fork(…) & exec(…)
1 |
|
fork()调用一次,返回两次
第一次在父进程返回,返回的是新生进程的ID
第二次在新生进程返回0
两次返回的意义非常明显,为了区分当前进程是父进程还是子进程,以此来执行不同的操作
fork的两种常见用法
创建一个自身副本
执行其他程序
1 |
|
sockaddr_in
1 | struct sockaddr_in { |
test code
Server.cpp
1 |
|
Client
1 |
|