在使用select管理服务器连接的时候:
注意1:select是可中断睡眠函数,需要屏蔽信号
注意2:必须获取select的返回值nread,每次处理完一个事件,nread需要-1
注意3:如果客户端的连接超过连接池的大小,需要关闭客户端连接
注意4:获取最大套接字的方法是每次有客户端连接过来时,在和maxfd比较,这样就不用每次select之前都遍历池,查找最大值
//serhelp.h
#ifndef _vxser
#define _vxser
#ifdef __cplusplus
extern "C"
{
#endif
/**
* sersocket_init - socket初始化
* @listenfd:文件描述符
* 成功返回0,失败返回错误码
* */
int sersocket_init(int *listenfd);
/**
* listen_socket - 绑定端口号,监听套接字
* @listenfd:文件描述符
* @port:绑定的端口号
* 成功返回0,失败返回错误码
* */
int listen_socket(int listenfd, int port);
/**
* run_server - 运行服务器
* @listenfd:文件描述符
* */
void run_server(int listenfd);
#ifdef __cplusplus
extern "C"
}
#endif
#endif
//serhelp.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "commsocket.h"
/**
* sersocket_init - socket初始化
* @listenfd:文件描述符
* 成功返回0,失败返回错误码
* */
int sersocket_init(int *listenfd)
{
int ret = 0;
if (listenfd == NULL)
{
ret = Sck_ParamErr;
printf("sersocket_init() params not correct !
");
return ret;
}
//初始化socket环境
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
ret = Sck_BaseErr;
perror("socket() err");
return ret;
}
*listenfd = fd;
return ret;
}
/**
* listen_socket - 绑定端口号,监听套接字
* @listenfd:文件描述符
* @port:绑定的端口号
* 成功返回0,失败返回错误码
* */
int listen_socket(int listenfd, int port)
{
int ret = 0;
if (listenfd < 0 || port < 0 || port > 65535)
{
ret = Sck_ParamErr;
printf("listen_socket() params not correct !
");
return ret;
}
//reuse addr
int optval = 1;
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval));
if (ret == -1)
{
ret = Sck_BaseErr;
perror("setsockopt() err");
return ret;
}
//bind
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = bind(listenfd, (struct sockaddr *) &addr, sizeof(addr));
if (ret == -1)
{
ret = Sck_BaseErr;
perror("bind() err");
return ret;
}
//listen
ret = listen(listenfd, SOMAXCONN);
if (ret == -1)
{
ret = Sck_BaseErr;
perror("listen() err");
return ret;
}
return ret;
}
/**
* handler - 信号捕捉函数
* @sign:信号码
* */
void handler(int sign)
{
if (sign == SIGPIPE)
{
printf("server accept SIGPIPE !
");
}
}
/**
* run_server - 运行服务器
* @listenfd:文件描述符
* */
void run_server(int listenfd)
{
int ret = 0;
//屏蔽SIGPIPIE信号
if (signal(SIGPIPE, handler) == SIG_ERR)
{
printf("signal() failed !
");
return;
}
//定义文件描述符集合
fd_set allsets;
FD_ZERO(&allsets);
fd_set readfds;
FD_ZERO(&readfds);
//定义客户端套接字池
char socketPond[128] = { 0 };
int i = 0;
for (i = 0; i < 128; i++)
{
socketPond[i] = -1;
}
//定义池子最后一个元素的下标
int maxindex = 0;
//定义文件描述符中值最大的fd
int maxfd = listenfd;
//将监听套接字加入到集合中
FD_SET(listenfd, &allsets);
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(struct sockaddr_in);
//定义接收缓冲区
char buf[MAXBUFSIZE] = { 0 };
int buflen = 0;
int conn = 0;
int nread = 0;
while (1)
{
readfds = allsets;
do
{
nread = select(maxfd + 1, &readfds, NULL, NULL, NULL);
} while (nread == -1 && errno == EINTR);//屏蔽信号(重点)
if (nread == -1)
{
perror("select() err");
return;
} else if (nread == 0)
{
//超时
continue;
} else if (nread > 0)
{
//执行操作
//1.处理服务器监听套接字
if (FD_ISSET(listenfd, &readfds))
{
//客户端有连接完成三次握手
memset(&peeraddr, 0, sizeof(struct sockaddr_in));
peerlen = sizeof(struct sockaddr_in);
conn = accept(listenfd, (struct sockaddr *) &peeraddr,
&peerlen);
if (conn == -1)
{
perror("accept() err");
break;
}
printf("accept from %s
", inet_ntoa(peeraddr.sin_addr));
//将客户端套接字加入池子中
for (i = 0; i < 128; i++)
{
if (socketPond[i] == -1)
{
socketPond[i] = conn;
//数组最大下标后移
if (maxindex <= i)
maxindex = i + 1;
break;
}
}
//如果超过最大连接数,直接关闭连接(重点)
if (i == 128)
{
close(conn);
continue;
}
//将客户端套接字加入到监听集合中
FD_SET(conn, &allsets);
//每新加一个连接,就更新最大套接字(重点)
if(conn>maxfd)
maxfd=conn;
//需要处理的事件数-1(重点)
if (--nread <= 0)
continue;
}
//2.客户端读事件
for (i = 0; i < maxindex; i++)
{
if (socketPond[i] == -1)
continue;
if (FD_ISSET(socketPond[i], &readfds))
{
//接收客户端信息
memset(buf, 0, sizeof(buf));
buflen = MAXBUFSIZE;
ret = socket_recv(socketPond[i], buf, &buflen);
if (ret == -1)
{
//接收信息出错,关闭套接字
close(socketPond[i]);
//将该套接字移除池子
socketPond[i] = -1;
//将该套接字移除监听集合
FD_CLR(conn, &allsets);
} else
{
//打印信息
fputs(buf, stdout);
//向客户端发送数据
ret = socket_send(socketPond[i], buf, buflen);
if (ret == -1)
{
//发送数据出错,关闭套接字
close(socketPond[i]);
//将该套接字移除池子
socketPond[i] = -1;
//将该套接字移除监听集合
FD_CLR(conn, &allsets);
}
}
//处理的事件数-1
if (--nread <= 0)
break;
}
}
}
}
return;
}
//服务器
#include "serhelp.h"
#include <stdio.h>
#include "commsocket.h"
int main()
{
int ret = 0;
int sockfd = 0;
//初始化socket
ret = sersocket_init(&sockfd);
if (ret != 0)
{
printf("error message:%s
", strsockerr(ret));
return -1;
}
//监听
ret = listen_socket(sockfd, 8080);
if (ret != 0)
{
printf("error message:%s
", strsockerr(ret));
return -1;
}
//运行
run_server(sockfd);
return 0;
}
.SUFFIXES:.c .o
CC=gcc
SRCS1=mserver.c
serhelp.c
commsocket.c
sockhelp.c
OBJS1=$(SRCS1:.c=.o)
EXEC1=mser
SRCS2=mclient.c
clthelp.c
commsocket.c
sockhelp.c
OBJS2=$(SRCS2:.c=.o)
EXEC2=mcl
start:$(OBJS1) $(OBJS2)
$(CC) -o $(EXEC1) $(OBJS1)
$(CC) -o $(EXEC2) $(OBJS2)
@echo "--------OK-----------"
.c.o:
$(CC) -Wall -g -o $@ -c $<
clean:
rm -f $(OBJS1)
rm -f $(OBJS2)
rm -f $(EXEC1)
rm -f $(EXEC2)