参考《TCP/IP网络编程第12章》
- select函数
- 关键宏
FD_ZERO(fd_set *fdset)
:将 fd_set 变量所指的位全部初始化成0FD_SET(int fd,fd_set *fdset)
:在参数 fdset 指向的变量中注册文件描述符 fd 的信息FD_SLR(int fd,fd_set *fdset)
:从参数 fdset 指向的变量中清除文件描述符 fd 的信息FD_ISSET(int fd,fd_set *fdset)
:若参数 fdset 指向的变量中包含文件描述符 fd 的信息,
则返回「真」
- select函数定义
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set *readset, fd_set *writeset,
fd_set *exceptset, const struct timeval *timeout);
成功时返回大于 0 的值,失败时返回 -1
maxfd: 监视对象文件描述符数量
readset: 将所有关注「是否存在待读取数据」的文件描述符注册到 fd_set 型变量,并传递其地址值。
writeset: 将所有关注「是否可传输无阻塞数据」的文件描述符注册到 fd_set 型变量,并传递其地址值。
exceptset: 将所有关注「是否发生异常」的文件描述符注册到 fd_set 型变量,并传递其地址值。
timeout: 调用 select 函数后,为防止陷入无限阻塞的状态,传递超时(time-out)信息
返回值: 发生错误时返回 -1,超时时返回0,因发生关注的时间返回时,返回大于0的值,该值是发生事件的文件描述符数。
- 服务器端代码
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::string;
using std::endl;
#define BUFFSIZE 30
#define DEFAULT_PORT 9190 // 指定端口为 9190
#define LOG_BUFFSIZE 65536
int main()
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
struct timeval timeout;
fd_set reads, cpy_reads;
socklen_t adr_sz;
int fd_max, str_len, fd_num, i;
char buf[BUFFSIZE];
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
cout << "server socket:" << serv_sock << endl;
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(DEFAULT_PORT);
//调用 bind 函数分配ip地址和端口号
if(bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
cout << "Bind error" << errno << ": " << strerror(errno) << endl;
//调用 listen 函数将套接字转为可接受连接状态
if(listen(serv_sock, 5) == -1)
cout << "Listen error" << errno << ": " << strerror(errno) << endl;
FD_ZERO(&reads);
FD_SET(serv_sock, &reads); //注册服务端套接字
fd_max = serv_sock;
while(1)
{
cpy_reads = reads;
timeout.tv_sec = 5;
timeout.tv_usec = 5000;
if((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, &timeout)) == -1)
break;
if(fd_num == 0)
continue;
for(i = 0; i < fd_max + 1; ++i)
{
if(FD_ISSET(i, &cpy_reads)) //查找发生变化的套接字文件描述符
{
if(i == serv_sock)//如果是服务端套接字时,受理连接请求
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
FD_SET(clnt_sock, &reads);//注册一个clnt_sock
if(fd_max < clnt_sock) fd_max = clnt_sock;
cout << "connected client: " << clnt_sock<< endl;
}
else//不是服务端套接字时
{
str_len = read(i, buf, BUFFSIZE); //i指的是当前发起请求的客户端
if(str_len == 0)
{
FD_CLR(i, &reads);
close(i);
cout << "closed cilent: " << i << endl;
}
else{
write(i, buf, BUFFSIZE);
}
}
}
}
}
close(serv_sock);
return 0;
}
- 客户端代码
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::string;
using std::endl;
#define BUFFSIZE 1024
#define SERVER_IP "127.0.0.1" // 指定服务端的IP
#define SERVER_PORT 9190 // 指定服务端的port
int main()
{
int sock;
char message[BUFFSIZE];
int str_len;
struct sockaddr_in serv_adr;
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1) cout << "socket() error" << endl;
bzero(&serv_adr, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = inet_addr(SERVER_IP);
serv_adr.sin_port = htons(SERVER_PORT);
if(connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
cout << "connect() error: " << errno << " " << strerror(errno) << endl;
else
cout << "connected............" << endl;
while(1)
{
cout << "Input message(Q to quit): " << endl;
cin >> message;
if(!strcmp(message, "q") || !strcmp(message, "Q"))
break;
write(sock, message, strlen(message));
str_len = read(sock, message, BUFFSIZE - 1);
// message[str_len] = 0;
cout << "Message from server:" << message << endl;
}
close(sock);
return 0;
}