大致描述一下。先定义 fd_set 要监视的读事件readfds,FD_SET将sockfd放到readfds事件里,更新文件最大描述符,select函数返回值是返回准备好的文件描述符个数(这里不考虑出错和超时),根据返回值for循环遍历文件描述符FD_ISSET挨个判断文件描述符有没有准备好。如果有准备好的文件描述符,再判断是不是sockfd。如果是sockfd表示有新客户端连接,这里就建立accept连接,acceptfd加入到事件里,并更新最大文件描述符。如果是其他的文件描述符表示有客户端发来消息,这里就recv接收消息并处理。、有客户端退出或断开连接就删除该文件描述符FD_CLR。
server.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define ERRLOG(ermsg) \
do { \
printf("%s %s %d:", __FILE__, __func__, __LINE__); \
perror(ermsg); \
exit(-1); \
} while (0)
int main(int argc, const char* argv[])
{
if (argc != 3) {
printf("retype!\n");
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
ERRLOG("socket error");
}
// 填充服务器信息结构体
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t serveraddr_len = sizeof(serveraddr);
if (-1 == bind(sockfd, (struct sockaddr*)&serveraddr, serveraddr_len)) {
ERRLOG("bind error");
}
if (-1 == listen(sockfd, 10)) {
ERRLOG("listen error");
}
// 定义客户端消息结构体
struct sockaddr_in clientaddr;
memset(&clientaddr, 0, sizeof(clientaddr));
socklen_t clientaddr_len = sizeof(clientaddr);
int acceptfd; // accept函数返回值
int maxfd;
int ret; // select返回值
int i;
char buf[128] = { 0 };
int recvnum; // recv函数返回值
// 创建要监视的文件描述符集合
fd_set readfds; // 母本
fd_set readfdscopy; // 副本
FD_ZERO(&readfds); // 清空集合初始化
FD_ZERO(&readfdscopy); // 清空集合初始化
// 把sockfd添加到要监视的集合中
FD_SET(sockfd, &readfds);
// 更新最大文件描述符
maxfd = maxfd > sockfd ? maxfd : sockfd;
while (1) {
readfdscopy = readfds;
ret = select(maxfd + 1, &readfdscopy, NULL, NULL, NULL);
if (ret == -1) { // select 返回值-1表示失败
ERRLOG("select error");
} else { // select返回值为0表示超时,这里先不考虑,程序到这里就表示就准备好的文件描述符
// 遍历文件描述符 判断文件描述符有没有准备好
for (i = 3; i < maxfd + 1 && ret != 0; i++) {
if (FD_ISSET(i, &readfdscopy)) { // fd_isset判断文件描述符是不是在集合里,如果 进来就表示有文件描述符准备好了
ret--; // 进入if就表示是准备好的文件描述符,ret-1
// 再判断是sockfd还是acceptfd
if (i == sockfd) {
// 进到这里表示有新的客户端连接
acceptfd = accept(sockfd, (struct sockaddr*)&clientaddr,
&clientaddr_len);
if (acceptfd == -1) {
ERRLOG("accept error");
}
printf("客户端[%d]连接到服务器\n", acceptfd);
// 将新来的acceptfd放到集合里
FD_SET(acceptfd, &readfds);
// 更新最大文件描述符
maxfd = maxfd > acceptfd ? maxfd : acceptfd;
} else {
memset(buf, 0, sizeof(buf));
// 进到这里表示有客户端发来消息
recvnum = recv(i, buf, sizeof(buf), 0);
if (recvnum == -1) {
ERRLOG("recv error");
} else if (recvnum == 0) {
printf("客户端[%d]断开连接\n", i);
close(i);
FD_CLR(i, &readfds); // 从集合中删除
continue; // 退出本次for循环
}
if (!strcmp(buf, "quit")) {
printf("客户端[%d]退出了\n", i);
close(i);
FD_CLR(i, &readfds); // 从集合中删除
continue; // 退出本次for循环
}
printf("客户端[%d]发来数据[%s]\n", i, buf);
strcat(buf, "--cmcc");
if (-1 == send(i, buf, sizeof(buf), 0)) {
ERRLOG("send error");
}
}
}
}
}
}
return 0;
}
client.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define ERRLOG(ermsg) \
do { \
printf("%s %s %d:", __FILE__, __func__, __LINE__); \
perror(ermsg); \
exit(-1); \
} while (0)
int main(int argc, const char* argv[])
{
if (argc != 3) {
printf("Usage : %s <IP> <PORT>\n", argv[0]);
return -1;
}
// 1创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET:IPV4,SOCK_STREAM:TCP
if (-1 == sockfd) {
ERRLOG("socket error");
}
// 2填充服务器网络信息结构体
struct sockaddr_in serveraddr = {
.sin_family = AF_INET,
.sin_port = htons(atoi(argv[2])), // 端口号 argv[2]输入进来的是char *字符串,需要转换成数用atoi
.sin_addr.s_addr = inet_addr(argv[1]), // IP地址
};
socklen_t serveraddr_len = sizeof(serveraddr);
char buf[128];
if (connect(sockfd, (struct sockaddr*)&serveraddr, serveraddr_len) == -1) {
ERRLOG("connect error");
}
printf("与服务器连接成功\n");
while (1) {
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
if (-1 == send(sockfd, buf, sizeof(buf), 0)) {
ERRLOG("send error");
}
int ret = recv(sockfd, buf, sizeof(buf), 0);
if (ret == -1) {
ERRLOG("recv error");
}
if (ret == 0) {//服务器断开连接
break;
}
printf("接收的数据%s\n", buf);
}
close(sockfd);
return 0;
}