基于UDP的网络聊天室的使用(select)完成的服务器端
#include<head.h>
typedef struct de
{
char name[10];
struct sockaddr_in cin;
struct de* next;
}*linklist;
//创建节点
linklist a_creat()
{
linklist p=(linklist)malloc(sizeof(struct de));
p->next=NULL;
bzero(p->name,sizeof(p->name));
return p;
}
//判断是否是新用户上线
linklist panduan(char *buf,struct sockaddr_in cin,linklist head,int sfd)
{
linklist p=head;
while(p->next!=NULL)
{
if(p->cin.sin_addr.s_addr==cin.sin_addr.s_addr && p->cin.sin_port==cin.sin_port)
{
return head;
}
p=p->next;
}
linklist q=a_creat();
q->next=head;
q->cin=cin;
strcpy(q->name,buf);
head=q;
char sbuf[20]="";
strcpy(sbuf,q->name);
strcat(sbuf,"上线");
printf("%s\n",sbuf);
while(q->next!=NULL)
{
q=q->next;
sendto(sfd,sbuf,sizeof(sbuf),0,(struct sockaddr*)&(q->cin),sizeof(q->cin));
}
return head;
}
//释放内存,并且删除该节点
linklist free_t(linklist q,linklist head)
{
if(q==head)
{
head=q->next;
q->next=NULL;
free(q);
return head;
}
linklist p=head;
while(p->next!=q)
{
p=p->next;
}
p->next=q->next;
q->next=NULL;
free(q);
return head;
}
int main(int argc, const char *argv[])
{
//1、创建套接字文件
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
int reuse=1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用成功\n");
//2、绑定
//2.1填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(8888);
sin.sin_addr.s_addr=inet_addr("192.168.122.22");
//2.2绑定工作
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
//3.定义变量存放客户端地址信息结构体
struct sockaddr_in cin;
socklen_t socklen=sizeof(cin);
int newfd = -1;
fd_set readfds,tempfds;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
linklist head=a_creat();
linklist Mhead=head;
while(1)
{
Mhead=head;
tempfds=readfds;
int res=select(sfd+1,&tempfds,NULL,NULL,NULL);
if(res==-1)
{
perror("select error");
return -1;
}
else if(res==0)
{
printf("timeout\n");
return -1;
}
if(FD_ISSET(0,&tempfds))//服务器发送消息
{
char rbuf[128]="";
bzero(rbuf,sizeof(rbuf));
scanf("%s",rbuf);
printf("发送成功\n");
linklist p=head;
while(p->next!=NULL)
{
sendto(sfd,rbuf,sizeof(rbuf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
p=p->next;
}
if(strcmp(rbuf,"quit")==0)
{
close(sfd);
printf("服务器下线\n");
return 0;
}
}
if(FD_ISSET(sfd,&tempfds))
{
char sbuf[128]="";
recvfrom(sfd,sbuf,sizeof(sbuf),0,(struct sockaddr*)&cin,&socklen);
head=panduan(sbuf,cin,head,sfd);//判断是否是有用户上线
if(head==Mhead)//如果头指针没发生改变,说明是链表中的用户发来消息
{
linklist yy=head;
while(yy->next!=NULL)
{
if(yy->cin.sin_addr.s_addr==cin.sin_addr.s_addr && yy->cin.sin_port==cin.sin_port)//找到用户对应的名字,并且将消息转发给其他用户
{
char dbuf[128]="";
strcpy(dbuf,yy->name);
if(strcmp(sbuf,"quit")==0)//判断该用户是否下线
{
strcat(dbuf,"下线");
head=free_t(yy,head);
linklist ss=head;
while(ss->next!=NULL)
{
if(ss!=yy)
{
sendto(sfd,dbuf,sizeof(dbuf),0,(struct sockaddr*)&(ss->cin),sizeof(ss->cin));
}
ss=ss->next;
}
break;
}
strcat(dbuf,":");
strcat(dbuf,sbuf);
printf("[%d]%s\n",ntohs(yy->cin.sin_port),dbuf);
linklist ss=head;
while(ss->next!=NULL)
{
if(ss!=yy)
{
sendto(sfd,dbuf,sizeof(dbuf),0,(struct sockaddr*)&(ss->cin),sizeof(ss->cin));
}
ss=ss->next;
}
break;
}
yy=yy->next;
}
}
}
}
return 0;
}
使用多进程完成的客户端
#include<head.h>
void handle(int arg)
{
if(arg==SIGCHLD)
{
while(waitpid(-1,NULL,WNOHANG)>0);
}
}
int main(int argc, const char *argv[])
{
if(signal(SIGCHLD,handle)==SIG_ERR)
{
perror("signal error");
return -1;
}
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
int reuse=1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用成功\n");
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(8888);
sin.sin_addr.s_addr=inet_addr("192.168.122.22");
socklen_t socklen=sizeof(sin);
char buf[128]="";
printf("请输入登录用户名:");
scanf("%s",buf);
sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin));
pid_t pid=fork();
while(1)
{
if(pid>0)
{
//主进程输入并且发送消息
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin));
if(strcmp(buf,"quit")==0)
{
close(sfd);
printf("你已下线\n");
return 0;
}
}else if(pid==0)
{
//子进程接收消
bzero(buf,sizeof(buf));
recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&socklen);
printf("%s\n",buf);
}
}
return 0;
}
结果
![](https://i-blog.csdnimg.cn/blog_migrate/79f97d4ca311a12455714d2f6a041068.png)