乐趣区

select 服务器 客户端 缩水版

tcpserver.c
int main(int argc, char**argv)
{
int listenfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in serv_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
memset(&serv_addr,0,sizeof(serv_addr));
memset(&client_addr,0,sizeof(client_addr));

serv_addr.sin_addr.s_addr = INADDR_ANY; // 绑定所有 ip
serv_addr.sin_family=AF_INET;
serv_addr.sin_port = htons(PORT);
int opt = 1;
socklen_t optlen = sizeof(opt);

// 设置复用 ip
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,optlen) < 0){
perror(“setsockopt”);
return 0;
}

// 设置 ip port
if(bind(listenfd,(SA*)&serv_addr,sizeof(serv_addr)) < 0){
perror(“bind”);
return 0;
}

//BACKLOG = 10
if(listen(listenfd,BACKLOG) < 0){
perror(“listen”);
return 0;
}

// 一些变量,下面会用到
int nready = 0,client[FD_SETSIZE] , maxfd = listenfd , connfd = 0,maxi = -1;

// client 用于存储 客户描述符
for(int i = 0; i < FD_SETSIZE ; ++i)
client[i] = -1;
fd_set rset ,allset;
FD_ZERO(&allset);

// 把监听套接字先置位
FD_SET(listenfd,&allset);
int clientfd = -1 , n = 0 , i = 0;
char buf[BUFSIZ];
while(1){
//select 每次将修改 rset, 需要重置
rset = allset;
nready = select(maxfd+1,&rset,NULL,NULL,NULL);
printf(“nread : %d \n” , nready);

// 可能被信号打断
if(nready < 0){
perror(“select”);
continue;
}
// 客户链接 进来
if(FD_ISSET(listenfd,&rset)){
client_len = sizeof(client_addr);
connfd = accept(listenfd,(SA*)&client_addr,&client_len);
printf(“a client : %d\n” , connfd);

// 找个位置放进去
for(i = 0; i < FD_SETSIZE; ++i){
if(client[i] < 0) {
client[i] = connfd;
break;
}
}
// 服务器已满
if(FD_SETSIZE == i){
close(connfd);
puts(“server is full”);
}
else {

// 把客户 fd 放入监听集合中
FD_SET(connfd,&allset);
if(connfd > maxfd)
maxfd = connfd;
//client 索引
if(i > maxi)
maxi = i;
// 如果数量为 0 则不需要往下继续了
if(–nready == 0)
continue;
}
}

for(int i = 0 ; i <= maxi;++i){

if((clientfd = client[i]) <0 )
continue;

// 直到找到一个可读的 fd
if(FD_ISSET(clientfd,&rset)){
printf(“clientfd : %d is ready\n”, clientfd);

// 如果对断关闭了
if((n = read(clientfd,buf,BUFSIZ)) <= 0){
printf(“clientfd : %d closed\n”,clientfd);
// 清空当前 fd 所存在的地方
close(clientfd);
FD_CLR(clientfd,&allset);
client[i] = -1;
} else{
write(clientfd,buf,n);
}
if(–nready == 0)
break;
}
}
}

return 0;
}

tcpclient.c

void str_echo(int sockfd);
int max(int a, int b){
return a > b ? a : b;
}
int main(int argc, char**argv)
{
if(argc != 2){
puts(“ip addr”);
return 0;
}

int sockfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_port = htons(PORT);
sin.sin_family = AF_INET;

// 把字符串转成网络字节序
inet_pton(AF_INET,argv[1],&sin.sin_addr);

connect(sockfd,(SA*)&sin,sizeof(sin));
str_echo(sockfd);

return 0;
}

void str_echo(int sockfd){
int maxfd = sockfd;
int eof = 0 , readn = 0 , fd_num = 0, n= 0;
char buf[BUFSIZ];
fd_set rset;
FD_ZERO(&rset);

while(1)
{
//EOF ==》CTRL+D
if(0 == eof){
FD_SET(0,&rset);
}

//select 将修改 rset , 每次重置
FD_SET(sockfd,&rset);
maxfd = max(0,sockfd);
fd_num = select(maxfd+1,&rset,NULL,NULL,NULL);
printf(“fd_num : %d\n” , fd_num);

// 如果是套接字可读
if(FD_ISSET(sockfd,&rset)){
// 服务器关闭
if((n=read(sockfd,buf,BUFSIZ)) <= 0){
if(eof == 1){
puts(“server closed”);
break;
}
else {
puts(“server ter”);
break;
}

}
buf[n] = 0;
printf(“recv from serv:%s\n” , buf);
}

// 如果是输入端可读
if(FD_ISSET(0,&rset)){

// 如果 ctrl + d
if((n = read(0,buf,BUFSIZ)) <= 0){
printf(“client closing\n”);

// 先发送 Fin , 等服务端 close
shutdown(sockfd,SHUT_WR);
eof = 1;
FD_CLR(0,&rset);
continue;
}
write(sockfd,buf,n);
}
}
}

退出移动版