c++Socket学习——使⽤listen(),accept(),write(),read()函数
对于服务器端程序,使⽤ bind() 绑定套接字后,还需要使⽤ listen() 函数让套接字进⼊被动监听状态,再调⽤ accept() 函数,就可以随时响应客户端的请求了。
listen() 函数
通过 listen() 函数可以让套接字进⼊被动监听状态,它的原型为:
int listen(int sock, int backlog);//Linux
int listen(SOCKET sock, int backlog);//Windows
sock 为需要进⼊监听状态的套接字,backlog 为请求队列的最⼤长度。
所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。所以,执⾏accept的是被动套接字,执⾏connect的是主动套接字。
请求队列
当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,
待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。
缓冲区的长度(能存放多少个客户端请求)可以通过 listen() 函数的 backlog 参数指定,但究竟为多少并没有什么标准,可以根据你的需求来定,并发量⼩的话可以是10或者20。心中的世外桃源
如果将 backlog 的值设置为 SOMAXCONN,就由系统来决定请求队列长度,这个值⼀般⽐较⼤,可能是⼏百,或者更多。26个字母的顺序>燕尾兰
当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误,对于 Windows,客户端会收到WSAECONNREFUSED 错误。
注意:listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使⽤ accept() 函数。
accept() 函数
800字当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。它的原型为:
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);//Linux
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);//Windows
它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
accept() 返回⼀个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端⼝号,⽽ sock 是服务器端的套接字,⼤家注意区分。后⾯和客户端通信时,要使⽤这个新⽣成的套接字,⽽不是原来服务器端的套接字。
最后需要说明的是:listen() 只是让套接字进⼊监听状态,并没有真正接收客户端请求,listen() 后⾯的代码会继续执⾏,直到遇到笔记本电脑开机黑屏没反应怎么办
accept()。accept() 会阻塞程序执⾏(后⾯代码不能被执⾏),直到有新的请求到来。
减压Linux 不区分套接字⽂件和普通⽂件,使⽤ write() 可以向套接字中写⼊数据,使⽤ read() 可以从套接字中读取数据。
Linux下数据的接收和发送
前⾯我们说过,两台计算机之间的通信相当于两个套接字之间的通信,在服务器端⽤ write() 向套接字写⼊数据,客户端就能收到,然后再使⽤ read() 从套接字中读取出来,就完成了⼀次通信。
北京自修大学
write() 的原型为:
ssize_t write(int fd,const void*buf, size_t nbytes);
fd 为要写⼊的⽂件的描述符,buf 为要写⼊的数据的缓冲区地址,nbytes 为要写⼊的数据的字节数。
size_t 是通过 typedef 声明的 unsigned int 类型;
ssize_t 在 "size_t" 前⾯加了⼀个"s",代表 signed,
即 ssize_t 是通过 typedef 声明的 signed int 类型。
write() 函数会将缓冲区 buf 中的 nbytes 个字节写⼊⽂件 fd,成功则返回写⼊的字节数,失败则返回 -1。
read() 的原型为:教学教研
ssize_t read(int fd,void*buf, size_t nbytes);
fd 为要读取的⽂件的描述符,buf 为要接收数据的缓冲区地址,nbytes 为要读取的数据的字节数。
read() 函数会从 fd ⽂件中读取 nbytes 个字节并保存到缓冲区 buf,成功则返回读取到的字节数(但遇到⽂件结尾则返回0),失败则返回 -1。
Windows下数据的接收和发送
Windows 和 Linux 不同,Windows 区分普通⽂件和套接字,并定义了专门的接收和发送的函数。
从服务器端发送数据使⽤ nd() 函数,它的原型为:
int nd(SOCKET sock,const char *buf, int len, int flags);
sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数,flags 为发送数据时的选项。
返回值和前三个参数不再赘述,最后的 flags 参数⼀般设置为 0 或 NULL,初学者不必深究。
在客户端接收数据使⽤ recv() 函数,它的原型为:
int recv(SOCKET sock, char *buf, int len, int flags);