I/O多路复用(一):用select实现的多客户聊天的服务器
2012-08-23 20:57
441 查看
以下例子为用select实现的多客户聊天的服务器,客户端成功连接服务器之后,可以和其他客户进行群聊。
服务器代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
void main(void)
{
struct sockaddr_in ser;
struct sockaddr_in cli;
int ser_fd;
int cli_len = sizeof(cli);
bzero(&ser, sizeof(ser));
bzero(&cli, sizeof(cli));
ser.sin_family = AF_INET;
ser.sin_port = htons(9678);
ser.sin_addr.s_addr = htonl(INADDR_ANY);
ser_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(ser_fd, (struct sockaddr*)&ser, sizeof(ser));
listen(ser_fd, 5);
int i;
int client[FD_SETSIZE];
for(i = 0; i < FD_SETSIZE; i++){ //初始化客户端数组,数组用于存放客户端socket描述符
client[i] = -1;
}
fd_set rd;
fd_set allset;
FD_ZERO(&allset);
FD_ZERO(&rd);
FD_SET(ser_fd, &allset);
int res;
int maxfd = ser_fd; //最大的描述符
int confd; //连接后返回的socket描述符
int maxi = 0;
int cli_num = 0; //处于连接状态的客户端总数
int self = 0;
char buf[1024];
while(1){
rd = allset; //见本文末尾红色提示
res = select(maxfd + 1, &rd, NULL, NULL, NULL);
if(res < 0)
perror("select failed");
else if(0 == res)
printf("select time out\n");
else{
if(FD_ISSET(ser_fd, &rd)){
confd = accept(ser_fd, (struct sockaddr*)&cli, &cli_len);
if(-1 == confd){
perror("accept failed");
continue;
}
for(i = 0; i < FD_SETSIZE; i++){
if(client[i] < 0){
client[i] = confd;
break;
}
}
if(i > maxi)
maxi = i;
if(maxfd < confd ) //获取最大的描述符
maxfd = confd;
FD_SET(confd, &allset);
cli_num++;
printf("%d have connected.\n", confd);
printf("Now,the client num is %d\n", cli_num);
}
for(i = 0; i <= maxi; i++){
if(client[i] < 0)
continue;
if(FD_ISSET(client[i], &rd)){
res = read(client[i], buf, sizeof(buf));
if(res <= 0){ //读取失败,去除该套接字
perror("read failed");
FD_CLR(client[i], &allset);
cli_num--;
printf("client %d is out of connect\n", client[i]);
close(client[i]);
client[i] = -1;
}else if(res > 0){
self = client[i];
printf("from client %d : %s\n", client[i], buf);
for(i = 0; i <= maxi; i++){ //实现群发
if(client[i] < 0)
continue;
if(self == client[i])
continue;
if(0 >= send(client[i], buf, sizeof(buf), 0))
perror("send failed\n");
}
memset(buf, 0, sizeof(buf));
}
}
}
}
}
}
客户端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <stdlib.h>
void main(void)
{
struct sockaddr_in local;
struct hostent *he = gethostbyname("localhost");
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(9678);
local.sin_addr = *(struct in_addr*)he->h_addr;
int fd = socket(AF_INET, SOCK_STREAM, 0);
int res = connect(fd, (struct sockaddr*)&local, sizeof(local));
if(res < 0){
perror("connect failed");
exit(1);
}
if(res == 0)
printf("connect\n");
fd_set rd;
fd_set test;
FD_ZERO(&rd);
FD_ZERO(&test);
FD_SET(0, &rd);
FD_SET(fd, &rd);
char buf[1024] = {0};
while(1){
test = rd; //见本文末尾红色提示
res = select(fd + 1, &test, NULL, NULL, NULL);
if(res < 0)
perror("select failed");
else if(0 == res)
perror("select time out");
else{
memset(buf, 0, sizeof(buf));
if(FD_ISSET(0, &test)){
fgets(buf, sizeof(buf), stdin);
if(0 >= send(fd, buf, sizeof(buf), 0)){
perror("send failed\n");
break;
}
}
if(FD_ISSET(fd, &test)){
if(0 >= recv(fd, buf, sizeof(buf), 0)){
perror("recv failed\n");
break;
}
printf("recv : %s\n", buf);
}
}
}
FD_CLR(0, &rd);
FD_CLR(fd, &rd);
close(fd);
}
注意一点:调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件可读。
select系列的FD函数同样会修改fd_set的内容。
所以循环调用select时需用一个中间变量来储存初始fd_set的值。
select的3个缺点:1 连接数受限 2 查找配对速度慢 3数据由内核拷贝到用户态。
服务器代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
void main(void)
{
struct sockaddr_in ser;
struct sockaddr_in cli;
int ser_fd;
int cli_len = sizeof(cli);
bzero(&ser, sizeof(ser));
bzero(&cli, sizeof(cli));
ser.sin_family = AF_INET;
ser.sin_port = htons(9678);
ser.sin_addr.s_addr = htonl(INADDR_ANY);
ser_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(ser_fd, (struct sockaddr*)&ser, sizeof(ser));
listen(ser_fd, 5);
int i;
int client[FD_SETSIZE];
for(i = 0; i < FD_SETSIZE; i++){ //初始化客户端数组,数组用于存放客户端socket描述符
client[i] = -1;
}
fd_set rd;
fd_set allset;
FD_ZERO(&allset);
FD_ZERO(&rd);
FD_SET(ser_fd, &allset);
int res;
int maxfd = ser_fd; //最大的描述符
int confd; //连接后返回的socket描述符
int maxi = 0;
int cli_num = 0; //处于连接状态的客户端总数
int self = 0;
char buf[1024];
while(1){
rd = allset; //见本文末尾红色提示
res = select(maxfd + 1, &rd, NULL, NULL, NULL);
if(res < 0)
perror("select failed");
else if(0 == res)
printf("select time out\n");
else{
if(FD_ISSET(ser_fd, &rd)){
confd = accept(ser_fd, (struct sockaddr*)&cli, &cli_len);
if(-1 == confd){
perror("accept failed");
continue;
}
for(i = 0; i < FD_SETSIZE; i++){
if(client[i] < 0){
client[i] = confd;
break;
}
}
if(i > maxi)
maxi = i;
if(maxfd < confd ) //获取最大的描述符
maxfd = confd;
FD_SET(confd, &allset);
cli_num++;
printf("%d have connected.\n", confd);
printf("Now,the client num is %d\n", cli_num);
}
for(i = 0; i <= maxi; i++){
if(client[i] < 0)
continue;
if(FD_ISSET(client[i], &rd)){
res = read(client[i], buf, sizeof(buf));
if(res <= 0){ //读取失败,去除该套接字
perror("read failed");
FD_CLR(client[i], &allset);
cli_num--;
printf("client %d is out of connect\n", client[i]);
close(client[i]);
client[i] = -1;
}else if(res > 0){
self = client[i];
printf("from client %d : %s\n", client[i], buf);
for(i = 0; i <= maxi; i++){ //实现群发
if(client[i] < 0)
continue;
if(self == client[i])
continue;
if(0 >= send(client[i], buf, sizeof(buf), 0))
perror("send failed\n");
}
memset(buf, 0, sizeof(buf));
}
}
}
}
}
}
客户端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <stdlib.h>
void main(void)
{
struct sockaddr_in local;
struct hostent *he = gethostbyname("localhost");
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(9678);
local.sin_addr = *(struct in_addr*)he->h_addr;
int fd = socket(AF_INET, SOCK_STREAM, 0);
int res = connect(fd, (struct sockaddr*)&local, sizeof(local));
if(res < 0){
perror("connect failed");
exit(1);
}
if(res == 0)
printf("connect\n");
fd_set rd;
fd_set test;
FD_ZERO(&rd);
FD_ZERO(&test);
FD_SET(0, &rd);
FD_SET(fd, &rd);
char buf[1024] = {0};
while(1){
test = rd; //见本文末尾红色提示
res = select(fd + 1, &test, NULL, NULL, NULL);
if(res < 0)
perror("select failed");
else if(0 == res)
perror("select time out");
else{
memset(buf, 0, sizeof(buf));
if(FD_ISSET(0, &test)){
fgets(buf, sizeof(buf), stdin);
if(0 >= send(fd, buf, sizeof(buf), 0)){
perror("send failed\n");
break;
}
}
if(FD_ISSET(fd, &test)){
if(0 >= recv(fd, buf, sizeof(buf), 0)){
perror("recv failed\n");
break;
}
printf("recv : %s\n", buf);
}
}
}
FD_CLR(0, &rd);
FD_CLR(fd, &rd);
close(fd);
}
注意一点:调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件可读。
select系列的FD函数同样会修改fd_set的内容。
所以循环调用select时需用一个中间变量来储存初始fd_set的值。
select的3个缺点:1 连接数受限 2 查找配对速度慢 3数据由内核拷贝到用户态。
相关文章推荐
- select()函数(I/O多路复用)-并发服务器的实现
- 第十九篇:不为客户连接创建子进程的并发回射服务器(select实现)
- 不为客户连接创建子进程的并发回射服务器( select实现 )
- 使用select系统调用实现简单的TCP服务器
- Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例
- C#下如何实现服务器+客户端的聊天程序
- Perl实现的简单单机聊天服务器
- windows 下 select 配合socket实现多路复用
- Linux客户端与服务器相互实现聊天功能
- Python基于select实现的socket服务器
- Select服务器代码实现
- select和poll服务器实现(Linux)
- socket实现客户端聊天以及服务器消息推送
- Python中的TCP编程,实现客户端与服务器的聊天(socket)
- Linux Socket 多并发服务器开源代码:xSocketd 实现PPC/TPC/SELECT/POLL/EPOLL
- 使用Nodejs实现聊天服务器
- Android 使用Socket实现服务器与手机客户端的长连接二:多Client对一Server聊天
- windows 下 select 配合socket实现多路复用
- java socket 实现多个客户端通过服务器一对一聊天并实现文件传输
- 服务器Select模型的实现