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);
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。