服务器代码实现:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#define IP "192.168.8.117"
#define PORT 8888
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__:",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
int sfd=socket(AF_INET,SOCK_STREAM,0);
int reuse=1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0){
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速重用\n");
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){
ERR_MSG("bind");
return -1;
}
if(listen(sfd,128)<0){
ERR_MSG("listen");
return -1;
}
printf("服务器监听成功\n");
struct sockaddr_in cin;
struct sockaddr_in arr[1024-4];
socklen_t addrlen=sizeof(sin);
//创建一个集合
fd_set readfds;
fd_set tempfds;
//清空集合
FD_ZERO(&readfds);
FD_ZERO(&tempfds);
//将需要的文件描述符添加到集合中
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
//存储最大文件描述符
int maxfd=sfd;
int newfd;
char buf[128]="";
size_t len=sizeof(buf);
int s_res;
int res;
int k=0;
while(1){
tempfds=readfds;
s_res=select(maxfd+1,&tempfds,NULL,NULL,NULL);
if(s_res<0){
ERR_MSG("select");
return -1;
}
//运行到这,说明产生了事件,遍历集合判断是哪个文件描述符产生的事件
for(int i=0;i<=maxfd;i++){
//从0开始判断,判断i这个文件描述符是否在集合中,不在则继续遍历
if(0==(FD_ISSET(i,&tempfds))){
continue;
}
if(0==i){
printf("发送成功\n");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
send(newfd,buf,sizeof(buf),0);
}else if(sfd==i){
printf("触发客户端连接\n");
newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
//将每次获取到的信息结构体存储到数组中,newfd从4开始,把他存入数组第一个元素中
arr[newfd-4]=cin;
printf("%s %d:连接成功 newfd=%d\n",inet_ntoa(arr[newfd-4].sin_addr)\
,ntohs(arr[newfd-4].sin_port),newfd);
//将新生成的newfd添加到集合中
FD_SET(newfd,&readfds);
//更新maxfd
maxfd=maxfd>newfd?maxfd:newfd;
}else{
printf("触发客户端交互\n");
bzero(buf,sizeof(buf));
res=recv(i,buf,len,0);
if(res<0){
ERR_MSG("recv");
return -1;
}else if(0==res){
printf("newfd=%d 客户端关闭\n",i);
//关闭对应的文件描述符,并将其从集合中清除
close(i);
FD_CLR(i,&readfds);
//清除之后,应寻找在除了i的集合中,最大的文件描述符,并对maxfd重新赋值
for(int j=maxfd;j>0;j--){
if(FD_ISSET(j,&readfds)){
maxfd=j;
//判断客户端是否全部关闭,全部关闭则退出程序
if(j==sfd){
close(sfd);
return 0;
}
break;
}
}
}else{
//此时i代表的就是产生事件的newfd
printf("%s %d newfd:%d:%s\n",inet_ntoa(arr[i-4].sin_addr),\
htons(arr[i-4].sin_port),i,buf);
}
}
}
}
return 0;
}
客户端代码实现:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#define IP "192.168.8.117"
#define PORT 8888
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_:",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
ERR_MSG("sfd");
return -1;
}
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=inet_addr(IP);
sin.sin_port=htons(PORT);
socklen_t addrlen=sizeof(sin);
if(connect(sfd,(struct sockaddr*)&sin,addrlen)<0){
ERR_MSG("connect");
return -1;
}
printf("连接服务器成功\n");
fd_set readfds;
fd_set tempfds;
FD_ZERO(&readfds);
FD_SET(sfd,&readfds);
FD_SET(0,&readfds);
int maxfd=sfd;
char buf[128]="";
bzero(buf,sizeof(buf));
while(1){
tempfds=readfds;
if(select(sfd+1,&tempfds,NULL,NULL,NULL)<0){
ERR_MSG("select");
return -1;
}
//判断终端输入事件发生
if(FD_ISSET(0,&tempfds)){
scanf("%s",buf);
//终端输入quit,则客户端退出
if(0==strcmp(buf,"quit")){
break;
}
send(sfd,buf,sizeof(buf),0);
printf("发送成功\n");
bzero(buf,sizeof(buf));
}
//判断服务器交互事件发生
if(FD_ISSET(sfd,&tempfds)){
if(0==recv(sfd,buf,sizeof(buf),0)){
printf("服务器关闭\n");
break;
}
printf("接收到:%s\n",buf);
bzero(buf,sizeof(buf));
}
}
close(sfd);
return 0;
}
如果客户端终端输入quit,客户端退出。
服务器检测到所有客户端断开连接,则服务器端退出。
服务器端断开连接,则客户端也退出。
效果展示:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)