嵌入式开发学习———Linux环境下网络编程学习(三)
作业:
这是一个基于TCP协议的简易聊天室程序,采用客户端/服务器架构,服务器使用select实现多路复用处理多个客户端的连接和消息转发,客户端通过双进程分别处理消息发送和接收功能,支持用户登录退出广播、群聊消息发送和系统消息推送等基本聊天功能,代码中定义了消息包结构体和用户信息链表来管理通信数据。
#include <myhead.h>//用户信息结构体
typedef struct Node
{char usrname[21]; //用户名struct sockaddr_in cin; //用户客户端网络地址信息struct Node *next; //指针域
}usr,*usrptr;//信息包结构体
typedef struct
{char type; //1、2、3char name[21]; //用户名char chatmsg[1024]; //发送的信息
}msgbag;char *mytime(void)
{}int main(int argc, const char *argv[])
{ //判断是否输入服务器网络信息if(argc<3){puts("请输入IP地址和端口号!!!");return -1;}//创建服务器等待链接套接字int sfd=socket(AF_INET,SOCK_STREAM,0);if(sfd==-1){ERROR_MSG("socket error");}//设置端口的短时多次绑定int reuse=1;if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){ERROR_MSG("setsockopt error");}//服务器网络地址结构体struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons((short)atoi(argv[2]));sin.sin_addr.s_addr=inet_addr(argv[1]);//绑定服务器网络地址if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1){ERROR_MSG("bind error");}//开启监听if(listen(sfd,128)==-1){ERROR_MSG("listen error");}//客户端信息结构体struct sockaddr_in cin;socklen_t addrlen=sizeof(cin);//定义一个文件描述符集合容器fd_set readfds;//集合容器清空FD_ZERO(&readfds);//将sfd、0文件描述符存入集合FD_SET(sfd,&readfds);FD_SET(0,&readfds);//临时容器fd_set tempfds;//新建文件描符变量int nwe_sfd=-1;//最大描述符int maxfd=sfd;//用户信息结构体链表头指针usrptr headptr=NULL;//用户信息结构体数组usrptr comptr[1024];msgbag bag;while(1){//临时容器获取内容tempfds=readfds;//IO复用int res=select(maxfd+1,&tempfds,NULL,NULL,NULL);if(res==-1){ERROR_MSG("select error");}//判断是否有新客户端发来连接请求内容if(FD_ISSET(sfd,&tempfds)){nwe_sfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);if(nwe_sfd==-1){ERROR_MSG("accept error");}//接收用户信息recv(nwe_sfd,&bag,sizeof(bag),0);if(bag.type==1){printf(" %s [%s:%d]成功登录\n",bag.name,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));}//用户信息结构体内存申请comptr[nwe_sfd]=(usrptr)malloc(sizeof(usr));//初始化strcpy(comptr[nwe_sfd]->usrname,bag.name);comptr[nwe_sfd]->cin=cin;//头插comptr[nwe_sfd]->next=headptr;headptr=comptr[nwe_sfd];//将新文件描述符写入容器FD_SET(nwe_sfd,&readfds);bag.type=3;//群发for(int i=4;i<=maxfd;i++){send(i,&bag,sizeof(bag),0);}//判断新文件描述符是否为最大的文件描述符if(nwe_sfd>maxfd){maxfd=nwe_sfd;}}//判断服务器是否有群发操作if(FD_ISSET(0,&tempfds)){//信息包初始化scanf("%s",bag.chatmsg);bag.type=2;strcpy(bag.name,"***system***");//群发for(int i=4;i<=maxfd;i++){if(strcmp(comptr[i]->usrname,bag.name)!=0){send(i,&bag,sizeof(bag),0);}}}//判断是否有客户端发送信息for(int i=4;i<=maxfd;i++){if(!FD_ISSET(i,&tempfds)){continue;}int res=recv(i,&bag,sizeof(bag),0);if(res==-1){close(i);close(sfd);ERROR_MSG("recv error");}if(bag.type==0){printf(" %s [%s:%d]已下线\n",bag.name,inet_ntoa(comptr[i]->cin.sin_addr),ntohs(comptr[i]->cin.sin_port));/*usrptr ptr=headptr,delptr;if(headptr==comptr[i]){free(headptr);headptr=NULL;comptr[i]=NULL;}else{while(ptr->next!=comptr[i]){ptr=ptr->next;}delptr=ptr->next;ptr->next=delptr->next;free(delptr);delptr=NULL;comptr[i]=NULL;}*/close(i);FD_CLR(i,&readfds);for(int j=maxfd;j>=0;j--){if(FD_ISSET(j,&tempfds)){maxfd=j;break;}}bag.type=0;strcpy(bag.chatmsg,"");//群发下线消息for(int i=4;i<=maxfd;i++){if(strcmp(comptr[i]->usrname,bag.name)!=0){send(i,&bag,sizeof(bag),0);}}continue;}else if(bag.type==2){//群发for(int i=4;i<=maxfd;i++){if(strcmp(comptr[i]->usrname,bag.name)!=0){send(i,&bag,sizeof(bag),0);}}}}}close(sfd);return 0;
}
#include <myhead.h>//信息包结构体
typedef struct
{char type; //1、2、3char name[21]; //用户名char chatmsg[1024]; //发送的信息
}msgbag;int main(int argc, const char *argv[])
{//判断是否输入服务器网络信息if(argc<3){puts("请输入IP地址和端口号!!!");return -1;}//创建客户端通信套接字int cfd=socket(AF_INET,SOCK_STREAM,0);if(cfd==-1){ERROR_MSG("socket error");}//服务器网络信息结构体struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons((short)atoi(argv[2]));sin.sin_addr.s_addr=inet_addr(argv[1]);socklen_t addrlen=sizeof(sin);msgbag bag;printf("请输入您的昵称>>");scanf("%s",bag.name);char name[21]="";strcpy(name,bag.name);//链接服务器if(connect(cfd,(struct sockaddr*)&sin,addrlen)==-1){ERROR_MSG("connect error");}else{bag.type=1;strcpy(bag.chatmsg,"");send(cfd,&bag,sizeof(bag),0);}pid_t pid1,pid2;pid1=fork();if(pid1==0){while(1){/*发信息进程*/msgbag bag;strcpy(bag.name,name);scanf("%s",bag.chatmsg);if(strcmp(bag.chatmsg,"quit")==0){bag.type=0;strcpy(bag.chatmsg,"");send(cfd,&bag,sizeof(bag),0);close(cfd);exit(EXIT_SUCCESS);}else{bag.type=2;send(cfd,&bag,sizeof(bag),0);}}}else{while(1){/*收消息进程*/msgbag bag;strcpy(bag.name,name);int res=recv(cfd,&bag,sizeof(bag),0);if(res==-1){close(cfd);ERROR_MSG("recv error");}if(bag.type==2){printf(" %s:[%s]\n",bag.name,bag.chatmsg);}else if(bag.type==3){printf("***%s已上线***\n",bag.name);}else if(bag.type==0){printf("***%s已下线***\n",bag.name);}}}return 0;
}