上一篇文章介绍了基本的select模型,使用select模型编写了一个功能超级简单的echo服务器,可以同时监听一个套接口(用户接受客户端连接)和标准输入。对于每一个客户端连接都是输出客户端的内容后,立马终止与客户端的连接,这一片文章中,同时监听标准输入,监听的套接口与已连接的客户端。
srv.c
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
int conn[FD_SETSIZE] = {0, 1, 2};
int connNum = 3;
int getPortFromParam(int argc, char *argv[]);
int openListenfd(int port);
int init_fdset(fd_set *fdset);
void echo_cmd();
void process_new_connection(int listenfd);
void process_cli(fd_set *fdset);
int main(int argc, char *argv[])
{
int port = getPortFromParam(argc, argv);
int listenfd = openListenfd(port);
conn[connNum++] = listenfd;
fd_set fdset;
while(1) {
int maxfd = init_fdset(&fdset);
int num = select(maxfd + 1, &fdset, NULL, NULL, NULL);
if(num == -1) { //error
perror("select error!");
exit(1);
} else if( num == 0) {
continue; //time out
} else {
if(FD_ISSET(0, &fdset)) {
echo_cmd(); //stdin
}
if(FD_ISSET(listenfd, &fdset) ) {
process_new_connection(listenfd);
}
process_cli(&fdset);
}
}
return 0;
}
int getPortFromParam(int argc, char *argv[])
{
if(argc != 2) {
printf("%s <port>\n", argv[0]);
exit(1);
}
int port = atoi(argv[1]);
}
int openListenfd(int port)
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd == -1) {
perror("create socket error");
exit(1);
}
struct sockaddr_in socksrv;
bzero(&socksrv, sizeof(socksrv));
socksrv.sin_family = AF_INET;
socksrv.sin_port = htons(port);
socksrv.sin_addr.s_addr = htonl(INADDR_ANY);
int result = bind(listenfd, (struct sockaddr*)&socksrv, sizeof(socksrv));
if(result == -1) {
perror("bind error");
exit(1);
}
result = listen(listenfd, 20);
if(result == -1) {
perror("listen error");
exit(1);
}
return listenfd;
}
int init_fdset(fd_set *fdset)
{
FD_ZERO(fdset);
int i, max = 0;
for(i = 0; i < connNum; i++) {
FD_SET(conn[i], fdset);
if(conn[i] > max)
max = conn[i];
}
return max;
}
void echo_cmd()
{
char buf[100] = {0};
int nread = read(STDIN_FILENO, buf, sizeof(buf));
if(nread == 0) {
printf("%s\n", "exit");
int i = 3;
for(; i < connNum; i++)
close(conn[i]);
exit(1);
} else if(nread > 0) {
printf("%s", buf);
}
}
void process_new_connection(int listenfd)
{
int fd = accept(listenfd, NULL, NULL);
if( fd == -1 ) {
perror("accept error!");
exit(1);
} else {
conn[connNum++] = fd;
printf("new connection:%d\n", fd);
}
}
void process_cli(fd_set *fdset)
{
int i;
for(i = 4; i<connNum; i++) {
if(FD_ISSET(conn[i], fdset)) {
char buf[100] = {0};
int nread = read(conn[i], buf ,sizeof(buf));
if(nread == 0) { //close
close(conn[i]);
printf("%d close!\n", conn[i]);
if(i != connNum - 1) {
conn[i] = conn[connNum - 1];
}
--i;
--connNum;
} else if(nread > 0){
buf[strlen(buf)-1] = 0;
printf("%d %s\n", conn[i], buf);
}
}
}
}
cli.c
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("%s <port>\n", argv[0]);
exit(1);
}
int port = atoi(argv[1]);
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in socksrv;
bzero(&socksrv, sizeof(socksrv));
socksrv.sin_family = AF_INET;
socksrv.sin_addr.s_addr = htonl(INADDR_ANY);
socksrv.sin_port = htons(port);
int result = connect(fd, (struct sockaddr*)&socksrv, sizeof(socksrv));
while(1) {
char buf[100] = {0};
int nread = read(0, buf, sizeof(buf));
if(nread == 0) {
close(fd);
break;
} else if(nread > 0) {
write(fd, buf, strlen(buf));
}
}
return 0;
}