您的位置:首页 > 理论基础 > 计算机网络

Linux网络编程-select实现多点连接的回射

2017-08-02 10:21 405 查看
server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <errno.h>

#define MAX_LOGIN 20     //设置客户端连接的最大数量
#define BUFLEN 1024      //设置缓存的大小
#define PORT 5000        //设置电脑端口

ssize_t readn(int fd,void *buf,size_t count);
ssize_t writen(int fd,void *buf,size_t count);
void ehandle(char *mesg);

int main()
{
int listenfd,connfd;
struct sockaddr_in seraddr,cliaddr;
int addrlen = 0;
int nread = 0;
char readbuf[BUFLEN];
int opt = 1;
fd_set rfds,readfds;
int maxfd,nready,i;
int clifd[FD_SETSIZE];

if(-1 == (listenfd = socket(AF_INET,SOCK_STREAM,0)))   //生成监听套接字
ehandle("socket");

bzero(&seraddr,sizeof(struct sockaddr_in));     //清零
seraddr.sin_family = AF_INET;                   //选择IPv4协议
seraddr.sin_port = htons(PORT);                  //选择5000端口
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);     //选择IP

setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));  //端口重用,避免因为端口处于time_wait状态而不能够bind成功(同一端口)

if(-1 == bind(listenfd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr))) //端口和套接字绑定
ehandle("bind");

if(-1 == listen(listenfd,MAX_LOGIN))    //开始监听
ehandle("listen");

for(i = 0;i < FD_SETSIZE-1;i++)          //用一组数组保存客户端套接字,FD_SETSIZE为select所能监听的最大数量,监听套接字占了一个
{
clifd[i] = -1;
}
FD_ZERO(&rfds);
FD_ZERO(&readfds);
FD_SET(listenfd,&readfds);
maxfd = listenfd;

while(1)
{
rfds = readfds;               //保险
do
{
nready = select(maxfd +1,&rfds,NULL,NULL,NULL);
}while(nready < 0 && EINTR == errno);  //避免因为其他中断引起select异常
if(-1 == nready)
ehandle("select");

if(FD_ISSET(listenfd,&rfds))             //处理监听到的客户端套接字
{
addrlen = sizeof(struct sockaddr);
if(-1 == (connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&addrlen)))
ehandle("accept");

printf("Connect IP:%s;PORT :%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));

for(i = 0;i < FD_SETSIZE;i++)
{
if(clifd[i] < 0)              //找到一个空的组员
{
clifd[i] = connfd;     //存储新的客户端套接字
FD_SET(connfd,&readfds); //加入select监听序列
if(connfd > maxfd)       //更新maxfd
maxfd = connfd;
break;
}
}

if(--nready <=0)                 //nready 减一,优化程序
continue;
}

for(i = 0;i <FD_SETSIZE;i++)           //循环查找被激活的客户端套接字,处理客户端的请求
{
if(clifd[i] > 0)
if(FD_ISSET(clifd[i],&rfds))  //因为用了两个集合,rfds和readfds,而这里一定要用rfds,错了就会只能与第一个链接成功的客户端通信。
{
memset(readbuf,0,sizeof(readbuf));  //清空缓存
connfd = clifd[i];
nread = readn(connfd,readbuf,sizeof(readbuf)); //读取客户端发来的字符串
if(-1 == nread)
ehandle("readn");
else if(0 == nread)                         //客户端关闭处理
{
printf("client closed!\n");
clifd[i] = -1;                    //清空该组员,以便能够存储新的客户端套接字
FD_CLR(connfd,&readfds);          //从select监听序列中清除
close(connfd);                    //关闭该客户端套接字
}
else                                      //正常处理
{
printf("Receive %s",readbuf);
if(-1 == writen(connfd,readbuf,sizeof(readbuf)))   //把收到的字符串回射给客户端
ehandle("writen");
}

if(--nready <= 0)                           //nready 减一,如果不为零,则继续处理其他的,同时被激活的客户端
break;
}
}
}
close(listenfd);
return 0;
}

void ehandle(char *mesg)   //错误处理
{
perror(mesg);
exit(1);
}

ssize_t readn(int fd,void *buf,size_t count)               //  读取固定字节数函数,避免粘包问题,这种处理方式效率不高
{
unsigned int nleft = count;
int nread = 0;
char *pbuf = (char*)buf;

while(nleft > 0)
{
nread = read(fd,pbuf,count);
if(-1 == nread)
{
if(EINTR == errno)
continue;
return -1;
}
else if(0 == nread)
break;

nleft -= nread;
pbuf += nread;
}
return (count - nleft);
}

ssize_t writen(int fd,void *buf,size_t count)     //写入固定字节数函数,避免粘包问题,这种处理方式效率不高

{
unsigned int nleft = count;
int nwrite = 0;
char *pbuf = (char *)buf;

while(nleft > 0)
{
nwrite = write(fd,pbuf,count);
if(-1 == nwrite)
{
if(EINTR == errno)
continue;
return -1;
}
else if(0 == nwrite)
continue;

nleft -= nwrite;
pbuf += nwrite;
}
return (count - nleft);
}
client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>

#define PORT 5000
#define IP "127.0.0.1"
#define BUFLEN 1024

void ehandle(char *mesg)
{
perror(mesg);
exit(1);
}

ssize_t readn(int fd,void* buf,size_t count)
{
unsigned int nleft = count;
int nread = 0;
char *pbuf = (char *)buf;

while(nleft > 0)
{
nread = read(fd,pbuf,count);
if(-1 == nread)
{
if(EINTR == errno)
continue;
return -1;
}
else if (0 == nread)
break;

nleft -= nread;
pbuf += nread;
}

return (count -nleft);
}

ssize_t writen(int fd,void *buf,size_t count)
{
unsigned int nleft = count;
int nwrite = 0;
char *pbuf = (char *)buf;

while(nleft > 0)
{
nwrite = write(fd,pbuf,count);
if(-1 == nwrite)
{
if(EINTR == errno)
continue;
return -1;
}
else if(0 == nwrite)
continue;

nleft -= nwrite;
pbuf += nwrite;
}
return (count - nleft);
}

int main()
{
int myfd;
struct sockaddr_in seraddr;
char strbuf[BUFLEN];
int nread,nwrite;
int maxfd;
int nready = 0;
fd_set readfds;

if(-1 == (myfd = socket(AF_INET,SOCK_STREAM,0)))
ehandle("socket");

bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(PORT);
//	seraddr.sin_addr.s_addr = inet_addr(IP);
inet_pton(AF_INET,IP,&seraddr.sin_addr,sizeof(struct in_addr));

if(-1 == connect(myfd,(struct sockaddr*)&seraddr,sizeof(struct sockaddr)))
ehandle("connect");

FD_ZERO
8ab9
(&readfds);
while(1)
{
FD_SET(STDIN_FILENO,&readfds);
FD_SET(myfd,&readfds);
maxfd = myfd;
nready = select(maxfd + 1,&readfds,NULL,NULL,NULL);

if(-1 == nready)
ehandle("select");
else if(0 == nready)
continue;
else
{
if(FD_ISSET(STDIN_FILENO,&readfds))
{
memset(strbuf,0,BUFLEN);
fgets(strbuf,BUFLEN,stdin);
nwrite = writen(myfd,strbuf,BUFLEN);
if(-1 == nwrite)
ehandle("writen");
}
if(FD_ISSET(myfd,&readfds))
{
memset(strbuf,0,BUFLEN);
nread = readn(myfd,strbuf,BUFLEN);
if(-1 == nread)
ehandle("readn");
else if(BUFLEN != nread)
{
printf("Server ending!\n");
break;
}
printf("Receive :%s",strbuf);
}
}
}
/*
while(1)
{
memset(strbuf,0,BUFLEN);
fgets(strbuf,BUFLEN,stdin);
nwrite = writen(myfd,strbuf,BUFLEN);
if(-1 == nwrite)
ehandle("writen");

memset(strbuf,0,BUFLEN);
nread = readn(myfd,strbuf,BUFLEN);
if(-1 == nread)
ehandle("readn");
else if(BUFLEN != nread)
{
printf("server ending!\n");
break;
}

printf("Receive :%s\n",strbuf);

}
*/
close(myfd);
return 0;
}


PS:

1 在debian 7上进行过简单测试,成功。

2 如果客户端或者服务器端用readn 和writen函数,则服务器端或客户端也必须用这个两个函数,否则不能正常回射。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐