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(&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函数,则服务器端或客户端也必须用这个两个函数,否则不能正常回射。