accept函数_基础套接字函数入门1

想不想自己写一个简单的QQ?想不想自己写一个聊天室?想不想知道2000年的人是如何上网的?本节讲解一些基础的网络函数,带你看看,编写一个完整的TCP客户端、服务端需要掌握哪些函数?之后,我们要开发自己的QQ。

1 socket函数

// sys/socket.h
/* 使用协议__protocol在域__domain中创建一个__type类型的新套接字。 
   如果__protocol为零,则自动选择一个。 
   返回新套接字的文件描述符,或者返回-1表示错误。
*/
extern int socket (int __domain, int __type, int __protocol) __THROW;
  • __domain:即family,AF_INETIPv4协议,AF_INET6IPv6协议
  • __typeSOCK_STREAM字节流套接字,SOCK_DGRAM数据报套接字,SOCK_RAW原始套接字
  • __protocolIPPROTO_TCPTCP传输协议,IPPROTO_UDPUDP传输协议

socket函数在成功时返回一个小的非负整数值,它与文件描述符类似,我们把它称为套接字描述符(socket descriptor),简称socketfd。

AF_前缀表示地址族,PF_前缀表示协议族 ,因为历史上曾想让一个协议族(PF)支持多个地址族(AF),用PF来创建套接字,用AF来创建套接字地址结构,然而就只是想想,没有实现。现在AF和PF的值是相等的。

// bits/socket.h
/* Protocol families.  */
#define PF_INET     2   /* IP protocol family.  */
#define PF_INET6    10  /* IP version 6.  */
/* Address families.  */
#define AF_INET     PF_INET
#define AF_INET6    PF_INET6 

2 connect函数

// sys/socket.h
#define __CONST_SOCKADDR_ARG   const struct sockaddr *
extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);
  • __fd:socket函数返回的套接字描述符
  • __addr:指向套接字结构地址的指针
  • __len:该套接字的大小

TCP客户用connect函数和TCP服务器建立连接。客户在调用connect函数前可以不调用bind函数,因为如果有需要,内核会确定本机IP地址,并选择一个临时端口作为源端口。

3 bind函数

// sys/socket.h
/* 把一个本地协议地址赋值给一个套接字 */
#define __CONST_SOCKADDR_ARG   const struct sockaddr *
extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len) __THROW;
  • 对于IPv4来说,协议地址是32位的IPv4地址和16位的端口号组合。
  • 对于IPv6来说,协议地址是128位的IPv6地址和16位的端口号组合。

绑定操作涉及3个对象:套接字,地址和端口。其中套接字是捆绑的主体,地址和端口是捆绑的客体。在套接字上绑定地址和端口表示:该地址和端口已经被套接字使用。

  • 如果指定端口号为0,那么内核在bind被调用的时候选择一个临时端口。
  • 如果指定IP地址是通配地址,那么内核将等到套接字已连接(TCP)或在套接字上发出数据报(UDP)时才选择一个本地IP地址。

对于IPv4来说,通配地址是0(INADDR_ANY),如果让内核帮套接字选择一个端口,那么必须注意,bind函数并不返回所选择的端口号。因为bind函数的_addr参数有const限定词,它无法返回所选的值。为了拿到内核选择的临时端口,必须调用getsockname函数来返回协议地址。

4 listen函数

// sys/socket.h
extern int listen (int __fd, int __n) __THROW;

listen函数仅有TCP服务器调用,它做两件事:

  1. 当socket函数创建一个套接字时,默认是主动套接字,listen函数将它变成被动套接字,指示内核应接受指向该套接字的连接请求。调用listen将导致套接字从CLOSED状态转换到LISTEN状态。
  2. 第二个参数__n规定了内核应该为相应套接字排队的最大连接个数。内核为任意一个监听套接字维护两个队列,一个叫未完成连接队列,一个叫已完成队列
  • 未完成连接队列:每个处于三次握手中的TCP连接,套接字处于SYN_RCVD状态。
  • 已完成连接队列:每个已完成三次握手的TCP连接,套接字处于ESTABLISHED状态。

这2个队列之和不能超过__n,否则就无法新建TCP连接,这就是SYN Flood攻击的原理。

5 accept函数

// sys/socket.h
extern int accept (int __fd, __SOCKADDR_ARG __addr,socklen_t *__restrict __addr_len);

accept函数由TCP服务器调用,用于从已完成连接队列的队头返回下一个已完成连接。如果该队列为空,那么进程被投入睡眠。(默认套接字为阻塞方式)

参数__addr__addr_len返回已连接的对端进程的协议地址,即返回客户端的协议地址。__addr_len是值-结果参数,返回内核存放在协议地址中实际的字节数。如果对客户端的协议地址不感兴趣,可以将指针置为NULL。

注意: 如果accept成功,返回值是又内核自动生成的全新描述符,称为已连接套接字。服务端与客户通信,将用这个套接字,服务完成后,这个套接字就被关闭。 而参数__fd称为监听套接字, 一个服务仅仅创建一个监听套接字

6 close函数

// /usr/include/unistd.h
extern int close (int __fd);

close函数一般可以用来关闭套接字,并终止TCP连接。但是在并发服务器中,close函数仅仅将套接字描述符引用计数减1。所以并不能一定在TCP连接上发送FIN报文,如果想在TCP连接上发送FIN报文,可以用shutdown函数。

参考文献:《UNIX网络编程 卷1:套接字联网API》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值