1.select()
select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型
#include
#include // 所在的头文件
int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);
1、maxfd:是需要监视的最大的文件描述符值+1
2、rdset:需要检测的可读文件描述符的集合
3、wrset:需要检测的可写文件描述符的集合
4、exset:需要检测的异常文件描述符的集合
5、timeout:指向timeval结构体的指针,通过传入的这个timeout参数来决定select()函数的三种执行方式
1.传入的timeout为NULL,则表示将select()函数置为阻塞状态,直到我们所监视的文件描述符集合中某个文件描述符发生变化是,才会返回结果。
2.传入的timeout为0秒0毫秒,则表示将select()函数置为非阻塞状态,不管文件描述符是否发生变化均立刻返回继续执行。
3.传入的timeout为一个大于0的值,则表示这个值为select()函数的超时时间,在timeout时间内一直阻塞,超过时间即返回结果 。
2.fd_set类型四个宏操作:
1、FD_ZERO(fd_set *fdset) 将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
2、FD_SET(fd_set *fdset) 用于在文件描述符集合中增加一个新的文件描述符。
3、FD_CLR(fd_set *fdset) 用于在文件描述符集合中删除一个文件描述符。
4、FD_ISSET(int fd,fd_set *fdset) 用于测试指定的文件描述符是否在该集合中
3.服务端
流程:
1、创建套接字,即调用socket(),根据需要选择参数类型
2、根据地址和端口号,绑定服务端,即调用bind()
3、将套接字设为监听套接字,并设置监听数,即调用listen()
4、开始检测监听套接字与客户端套接字信号,即调用select()
4、阻塞等待等待客户端连接请求,即调用accept()
5、接受发送消息,即调用recv(),send()函数
6、关闭套接字,即调用close()
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class Server
{
private:
int port; // 设置端口号
int serverfd; // 定义服务端套接字(这个最后作为了监听套接字)
int clientfd; // 定义客户端套接字
int backlog; // 设置最大客户端连接数,监听数
struct sockaddr_in serveraddr; // 定义服务端套接字地址结构体
struct sockaddr_in clientaddr; // 定义客户端套接字地址结构体
struct timeval tv; // 定义时间结构体
socklen_t clientlen; // 定义客户端套接字长度
public:
Server(int port = 10000):port(port),serverfd(-1),clientfd(-1),backlog(10) // 初始化相关参数
{
serverfd = socket(AF_INET, SOCK_STREAM, 0); // 初始化服务端套接字对象
//cout << serverfd << "\n";
if (serverfd == -1)
{
throw("Create socket error ");
}
serveraddr.sin_family = AF_INET; // 定义服务端地址为AF_INET协议族
serveraddr.sin_port = htons(port); // 定义服务端地址的端口号
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 定义服务端地址ip
int bind_val = bind(serverfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); // 绑定服务端
if (bind_val<0)
{
throw("Bind error...");
}
listen(serverfd, backlog); // 开始监听
}
void run()
{
fd_set readfds; // 可读套接字集合
backlog = 10; // 初始化客户端最大连接数
int maxfd; // 定义可读套接字中最大数
int arr[]; // 定义一个数据用于轮询各个客户端连接
for(int k = 0; k0)
{
FD_SET(arr[i],&readfds); // 将其余连接进来的客户端套接字加入可读套接字中,已保证接收数据
}
if (arr[i]>maxfd)
{
maxfd = arr[i]; // 确定套接字中的最大数
}
}
int select_val = select(maxfd+1, &readfds, NULL,NULL, &tv); // 开始监控套接字信号变化
if(select_val==-1)
{
cout << "Select error..." << "\n";
break;
}else if(select_val==0)
{
cout << "No response..." << "\n";
sleep(3);
continue;
}else
{
cout << "Receive response..." << "\n";
}
for(i=0;irun(); // 运行函数run()
}catch(const char* e)
{
cout << e << "\n";
}
delete s; // 删除指针
}
int main()
{
test();
}
4.客户端
流程:
1、创建套接字,即调用socket()
2、根据地址,端口号绑定地址,即调用bind()
3、发起连接服务端请求,即调用connect()
4、接收发消息,即recv(),send()
5、关闭套接字,close()
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class Client
{
private:
int port;
int serverfd;
struct sockaddr_in serveraddr;
public:
Client(int port = 10000):port(port),serverfd(-1)
{
serverfd = socket(AF_INET, SOCK_STREAM, 0);
if(serverfd == -1)
{
throw("create socket error");
}
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr.s_addr);
int ret1;
ret1 = connect(serverfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
}
void run()
{
char* data;
int ret2;
data = "hello";
ret2 = send(serverfd, data, strlen(data), 0);
}
~Client()
{
cout << "close client..." << "\n";
if (serverfd == -1)
{
close(serverfd);
}
}
};
int test()
{
try
{
Client *c = NULL;
c = new Client;
c->run();
delete c;
}catch(const char* e)
{
cout << e << "\n";
}
}
int main(int argc, char *argv[])
{
test();
}
以上代码亲测有效,可以多个客户端同时连接,并收发消息