select服务器模型
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define ERR_MSG(msg) \
do { \
fprintf(stderr, "line : %d\n", __LINE__); \
perror(msg); \
} while (0)
#define IP "192.168.31.246"
#define PORT 8888
int main(int argc, char const* argv[])
{
// 创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) {
ERR_MSG("socket error");
return -1;
}
printf("socket successful __%d__\n", __LINE__);
// 设置端口快速重用
int reuse = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
ERR_MSG("setsockopt error");
return -1;
}
printf("允许端口快速重用!\n");
//(必须)绑定服务器IP地址和端口
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
if (bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
ERR_MSG("bind error");
return -1;
}
printf("bind successful __%d__\n", __LINE__);
// 将套接字设置为被动监听状态
if (listen(sfd, 128) < 0) {
ERR_MSG("listen error");
return -1;
}
printf("listen successful __%d__\n", __LINE__);
int maxfd = sfd; // 最大文件描述符
fd_set readfds, readfds_temp;
int select_res; // select的返回值
int sendfd; // 发送文件描述符
char buf[128] = { 0 };
ssize_t res;
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
struct sockaddr_in SaveCin[1020]; // 存储读集合的文件描述符地址信息结构体
int newfd;
// 清空读集合
FD_ZERO(&readfds);
// 将所需的文件描述符写到读集合
FD_SET(0, &readfds);
FD_SET(sfd, &readfds);
while (1) {
readfds_temp = readfds;
select_res = select(maxfd + 1, &readfds_temp, NULL, NULL, NULL);
if (0 > select_res) {
ERR_MSG("select error");
return -1;
} else if (0 == select_res) {
printf("time out ……\n");
break;
}
for (size_t i = 0; i <= maxfd; i++) {
// 判断i是否在读集合中
if (FD_ISSET(i, &readfds_temp) == 0) {
continue;
}
// 键盘输入事件
if (0 == i) {
printf("触发键盘输入事件>>> ");
bzero(buf, sizeof(buf));
res = scanf("%d %s", &sendfd, buf);
while (getchar() != '\n')
;
// 判断输入格式
if (res != 2) {
fprintf(stderr, "请输入正确格式:fd string\n");
continue;
}
// 判断发送文件描述符是否合法
if (sendfd <= sfd || sendfd >= 1024 || !FD_ISSET(sendfd, &readfds)) {
fprintf(stderr, "sendfd = %d 是非法文件描述符\n", sendfd);
continue;
}
if (send(sendfd, buf, strlen(buf), 0) < 0) {
ERR_MSG("send error");
return -1;
}
printf("send to %d successful __%d__\n", sendfd, __LINE__);
} else if (sfd == i) { // 客户端连接事件
printf("触发客户端连接事件>>> ");
fflush(stdout);
if ((newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen)) < 0) {
ERR_MSG("accept error");
continue;
}
printf("from client-[%s : %d] newfd = %d 连接成功 __%d__\n",
inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);
SaveCin[newfd - 4] = cin;
// 将newfd加入到readfds读集合中
FD_SET(newfd, &readfds);
// 更新maxfd
maxfd = maxfd > newfd ? maxfd : newfd;
} else // 触发客户端交互事件
{
fprintf(stderr, "触发客户端交互事件>>> ");
// 接收数据
bzero(buf, sizeof(buf));
res = recv(i, buf, sizeof(buf), 0);
if (res < 0) {
ERR_MSG("recv error");
return -1;
} else if (res == 0) {
printf("from client-[%s : %d] newfd = %ld 客户端下线 __%d__\n",
inet_ntoa(SaveCin[i - 4].sin_addr), ntohs(SaveCin[i - 4].sin_port), i, __LINE__);
// 关闭文件描述符
close(i);
// 从读集合剔除文件描述符i
FD_CLR(i, &readfds);
// 更新maxfd
int j = maxfd;
for (; j >= 0; j--) {
if (FD_ISSET(j, &readfds)) {
break;
}
}
maxfd = j;
continue;
}
printf("from client-[%s : %d] newfd = %ld buf = %s __%d__\n",
inet_ntoa(SaveCin[i - 4].sin_addr), ntohs(SaveCin[i - 4].sin_port), i, buf, __LINE__);
}
}
}
// 关闭文件描述符
close(sfd);
return 0;
}
select客户端模型
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define ERR_MSG(msg) \
do { \
fprintf(stderr, "line : %d\n", __LINE__); \
perror(msg); \
} while (0)
#define IP "192.168.31.246"
#define PORT 8888
int main(int argc, char const* argv[])
{
// 1.创建流式套接字
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd < 0) {
ERR_MSG("socket error");
return -1;
}
printf("socket successful __%d__\n", __LINE__);
/* 2.1 填充服务器的地址信息结构体
真实的结构体根据地址族制定(即socket第一个参数确定)
*/
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
// 连接服务器
if (connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
ERR_MSG("connect error");
return -1;
}
printf("connect successful __%d__\n", __LINE__);
fd_set readfds, readfds_temp;
int select_res; // select的返回值
char buf[128] = { 0 };
ssize_t res;
// 清空读集合
FD_ZERO(&readfds);
// 将所需的文件描述符写到读集合
FD_SET(0, &readfds);
FD_SET(cfd, &readfds);
while (1) {
readfds_temp = readfds;
select_res = select(cfd + 1, &readfds_temp, NULL, NULL, NULL);
if (0 > select_res) {
ERR_MSG("select error");
return -1;
} else if (0 == select_res) {
printf("time out ……\n");
break;
}
// 触发键盘输入
if (FD_ISSET(0, &readfds_temp)) {
printf("触发键盘输入事件>>> ");
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
if (send(cfd, buf, strlen(buf), 0) < 0) {
ERR_MSG("send error");
return -1;
}
printf("send successful __%d__\n", __LINE__);
}
if (FD_ISSET(cfd, &readfds_temp)) {
printf("触发服务器交互事件>>> ");
memset(buf, 0, sizeof(buf));
res = recv(cfd, buf, sizeof(buf), 0);
if (res < 0) {
ERR_MSG("recv error");
return -1;
} else if (res == 0) {
printf("服务器下线\n");
break;
}
printf("buf = %s __%d__\n", buf, __LINE__);
}
}
close(cfd);
return 0;
}
poll客户端模型
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define ERR_MSG(msg) \
do { \
fprintf(stderr, "line : %d\n", __LINE__); \
perror(msg); \
} while (0)
#define IP "192.168.31.246"
#define PORT 8888
int main(int argc, char const* argv[])
{
// 1.创建流式套接字
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd < 0) {
ERR_MSG("socket error");
return -1;
}
printf("socket successful __%d__\n", __LINE__);
/* 2.1 填充服务器的地址信息结构体
真实的结构体根据地址族制定(即socket第一个参数确定)
*/
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
// 连接服务器
// 连接服务器
if (connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
ERR_MSG("connect error");
return -1;
}
printf("connect successful __%d__\n", __LINE__);
struct pollfd fds[2];
fds[0].fd = 0; // 指定要监测的是0号文件描述符
fds[0].events = POLLIN; // 指定要监测读事件
fds[1].fd = cfd; // 指定要监测的是cfd
fds[1].events = POLLIN; // 指定要监测读事件
int p_res;
char buf[128] = { 0 };
ssize_t res;
while (1) {
p_res = poll(fds, 2, -1);
if (p_res < 0) {
ERR_MSG("poll error");
return -1;
} else if (0 == p_res) {
printf("time out …… \n");
break;
}
if (fds[0].events & POLLIN) {
printf("触发键盘输入事件>>> ");
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
if (send(cfd, buf, strlen(buf), 0) < 0) {
ERR_MSG("send error");
return -1;
}
printf("send successful __%d__\n", __LINE__);
}
if (fds[1].events & POLLIN) {
printf("触发服务器交互事件>>> ");
memset(buf, 0, sizeof(buf));
res = recv(cfd, buf, sizeof(buf), 0);
if (res < 0) {
ERR_MSG("recv error");
return -1;
} else if (res == 0) {
printf("服务器下线\n");
break;
}
printf("buf = %s __%d__\n", buf, __LINE__);
}
}
return 0;
}