服务器端:
#include<myhead.h>
//存放消息的结构体
typedef struct Msgtpy
{
char type;
char name[40];
char msgtext[1024];
}message_t;
//存放各个客户端信息的结构体
typedef struct Node
{
struct sockaddr_in cin;
struct Node *next;
}*linklist;
//单链表节点创建
linklist Create()
{
linklist s=(linklist)malloc(sizeof(struct Node));
if(NULL==s)
{
return NULL;
}
s->next=NULL;
return s;
}
//单链表头插,加入新的客户端
linklist insert_head(linklist head,struct sockaddr_in cin)
{
//创建新节点s
linklist s=Create();
if(NULL==s)
{
return NULL;
}
s->cin=cin;
//判断链表是否为空
if(NULL==head)
{
head=s;
}
else
{
s->next=head;
head=s;
}
return head;
}
struct sockaddr_in cin;
linklist free_cli(linklist head)
{
//只有一个客户端时
if(head->next==NULL)
{
free(head);
head=NULL;
return head;
}
//有两个及以上客户端时
linklist p=head;
while(p->next)
{
if(memcmp(&(p->next->cin),&cin,sizeof(cin))==0)
{
linklist del=p->next;
p->next=del->next;
free(del);
del=NULL;
break;
}
else
{
p=p->next;
}
return head;
}
}
int main(int argc, const char *argv[])
{
//创建用于通信的套接字文件描述符
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1)
{
perror("socket error\n");
return -1;
}
//端口快速重用
int reuse = -1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error\n");
return -1;
}
//填充地址信息结构体、绑定
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(8888);
sin.sin_addr.s_addr=inet_addr("192.168.242.132");
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error\n");
return -1;
}
//定义容器接受对端地址信息结构体
struct sockaddr_in cin;
socklen_t socklen = sizeof(cin);
linklist head=NULL;//客户端链表
//创建文件描述符容器
fd_set readfds,tempfds;
//清空容器
FD_ZERO(&readfds);
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
int maxfd=sfd;
char lbuf[1024];
char cbuf[1024];
char qbuf[1024];
message_t msg;
while(1)
{
tempfds=readfds;//将要检测的容器临时复制一份
int res=select(maxfd+1,&readfds,NULL,NULL,NULL);//阻塞等待集合中的事件发生
if(res==-1)
{
perror("select error\n");
return -1;
}
else if(res==0)
{
printf("time out\n");//超时
return -1;
}
//sfd触发事件
if(FD_ISSET(sfd,&tempfds))
{
recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&cin,socklen);//接收客户端消息
switch(msg.type)
{
case('L')://登录消息
{
head=insert_head(head,cin);
linklist p=head;
bzero(lbuf,sizeof(lbuf));
printf("%s[%s:%d]登录服务器\n",msg.name,inet_ntoa(p->cin.sin_addr),ntohs(p->cin.sin_port));
p=p->next;
do
{
snprintf(lbuf,sizeof(lbuf),"%s[%s:%d]加入聊天室\n",msg.name,inet_ntoa(p->cin.sin_addr),ntohs(p->cin.sin_port));
sendto(sfd,&lbuf,sizeof(lbuf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
p=p->next;
}while(p->next);
};break;
case ('C')://服务器转发消息
{
linklist p=head;
p=p->next;
bzero(cbuf,sizeof(cbuf));
do
{
snprintf(cbuf,sizeof(cbuf),"%s[%s:%d]:%s\n",msg.name,inet_ntoa(p->cin.sin_addr),ntohs(p->cin.sin_port),msg.msgtext);
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr *)&(p->cin),sizeof(p->cin));
}while(p->next);
};break;
case ('Q')://客户端退出
{
linklist p=head;
p=p->next;
bzero(qbuf,sizeof(qbuf));
do
{
snprintf(qbuf,sizeof(qbuf),"%s[%s:%d]退出了聊天室\n",msg.name,inet_ntoa(p->cin.sin_addr),ntohs(p->cin.sin_port));
}while(p->next);
head=free_cli(head);
};break;
default:
{
printf("type error\n");
return -1;
}
}
}
char buf[128];
char wbuf[128];
if(FD_ISSET(0,&tempfds))
{
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
snprintf(wbuf,sizeof(wbuf),"*****system*****\n%s",buf);
linklist p=head;
while(p)
{
sendto(sfd,wbuf,sizeof(wbuf),0,(struct sockaddr *)&(p->cin),sizeof(p->cin));
p=p->next;
}
}
}
return 0;
}
客户端:
#include<myhead.h>
typedef struct Msgtpy
{
char type;
char name[40];
char msgtext[1024];
}message_t;
int main(int argc, const char *argv[])
{
message_t msg;
int cfd=socket(AF_INET,SOCK_STREAM,0);
if(cfd==-1)
{
perror("socket error\n");
return -1;
}
//服务器地址信息
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(8888);
sin.sin_addr.s_addr=inet_addr("192.168.242.132");
//客户端登录
printf("请输入姓名:");
fgets(msg.name,sizeof(msg.name),stdin);
msg.name[strlen(msg.name)-1]=0;
msg.type='L';
if(sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr *)&sin,sizeof(sin))==-1)
{
perror("sendto error\n");
return -1;
}
else
{
printf("成功加入了聊天室\n");
}
//创建文件描述符容器
fd_set readfds,tempfds;
//清空容器
FD_ZERO(&readfds);
FD_SET(0,&readfds);
FD_SET(cfd,&readfds);
int maxfd=cfd;
while(1)
{
tempfds=readfds;//将要检测的容器临时复制一份
int res=select(maxfd+1,&readfds,NULL,NULL,NULL);//阻塞等待集合中的事件发生
if(res==-1)
{
perror("select error\n");
return -1;
}
else if(res==0)
{
printf("time out\n");//超时
return -1;
}
if(FD_ISSET(0,&tempfds))
{
memset(msg.msgtext,0,sizeof(msg.msgtext));
read(0,msg.msgtext,sizeof(msg.msgtext));
msg.msgtext[strlen(msg.msgtext)-1]=0;
if(strcmp(msg.msgtext,"quit")==0)
{
msg.type='Q';
sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
printf("本客户端下线\n");
close(cfd);
return 0;
}
else
{
msg.type='C';
sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
}
}
if(FD_ISSET(cfd,&tempfds))
{
char buf[128];
recvfrom(cfd,buf,sizeof(buf),0,NULL,NULL);
printf("%s",buf);
fflush(stdout);
}
}
return 0;
}