一、问题引入
UNIX网络编程 卷1:套接字联网API(第三版) 第6章 介绍了I/O复用可以通过select()的单进程服务器与多客户端通信。
UNIX下可用的5中I/O模型:
- 阻塞式I/O
- 非阻塞式I/O
- I/O复用(select和poll)
- 信号驱动式I/O(SIGIO)
- 异步I/O(POSIX的aio_系列函数)
其中前面4种可以分为同步I/O,第五种为异步I/O。
二、解决过程
2-1 client 代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define IP "10.8.198.227"
#define PORT 8887
#define BUF_MAX_SIZE 1024
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
static int handle(int connect_fd, const char *socket)
{
char buf[BUF_MAX_SIZE], read_buf[BUF_MAX_SIZE];
int n;
int maxfdp1, stdineof = 0;
fd_set rset;
FD_ZERO(&rset);
while (1)
{
if (stdineof == 0)
FD_SET(fileno(stdin), &rset);
FD_SET(connect_fd, &rset);
maxfdp1 = max(fileno(stdin), connect_fd) + 1;
if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0)
{
perror("select error");
exit(1);
}
memset(buf, 0, BUF_MAX_SIZE);
if (FD_ISSET(connect_fd, &rset)) // socket is readable
{
n = read(connect_fd, buf, BUF_MAX_SIZE);
if (n == -1)
{
perror("read error");
exit(1);
}
else if (n == 0)
{
if (stdineof == 1)
return 1;
else
{
perror("server terminated prematurely");
exit(1);
}
}
memset(read_buf, 0, BUF_MAX_SIZE);
sprintf(read_buf, "%s:%s(%d Byte)\n", socket, buf, n);
write(fileno(stdout), read_buf, strlen(read_buf));
}
if (FD_ISSET(fileno(stdin), &rset)) // input is readable
{
n = read(fileno(stdin), buf, BUF_MAX_SIZE);
if (n == -1)
{
perror("read error");
exit(1);
}
else if (n == 0)
{
stdineof = 1;
shutdown(connect_fd, SHUT_WR);
FD_CLR(fileno(stdin), &rset);
continue;
}
if (buf[n-1] == '\n') // remove the character '\n'
{
buf[n-1] = '\n';
n = n - 1;
}
write(connect_fd, buf, n);
}
}
return 0;
}
int main(void)
{
int sockfd;
char buf[1024];
struct sockaddr_in server_addr;
char server_socket[128];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
{
printf("connect error \n");
return -1;
}
printf("server addr:%s por:%d\n",
inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),
ntohs(server_addr.sin_port));
snprintf(server_socket, sizeof(server_socket), "server socket (%s:%d)",
inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),
ntohs(server_addr.sin_port));
handle(sockfd, server_socket);
close(sockfd);
return EXIT_SUCCESS;
}
2-2 server 代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MYPORT 8887
#define BACKLOG 5
#define BUF_SIZE 1024
typedef struct CLIENT_CONN_ST
{
int connfd;
char socket[128];
struct sockaddr_in cliaddr;
} CLIENT_CONN_ST;
static CLIENT_CONN_ST g_client_conn[BACKLOG];
stat