服务器
#include <myhead.h>
#define SER_PORT 8080
#define SER_IP "10.102.145.16"
/*
#define SER_IP "192.168.122.153"
*/
#define LOGIN 0; //表示登录消息
#define CHAT 1; //表示聊天消息
#define LOGOUT 2; //表示退出消息
//定义关于客户端发送信息的结构体
typedef struct M{
int message_type; //表示所发送的消息类型
char user_name[20]; //表示用户名
char text[128]; //用来存储所发送的消息
}Msg,*pMsg;
enum A
{
FALSE=-1,
SUCCESS
};
typedef struct Node
{
char user_name[20];
int state;//表示这个客户端此时的状态,0为不在线,1为在线
struct sockaddr_in cin;//存储客户端的信息
struct Node *next;
}*p_cli_msg,cli_msg;
int null(p_cli_msg p) //判断链表是否为空函数
{
return p==NULL?FALSE:SUCCESS;
}
p_cli_msg create_node() //创建节点函数
{
p_cli_msg p=(p_cli_msg)malloc(sizeof(cli_msg));
if(null(p))
return NULL;
strcpy(p->user_name,"");
p->state=-1;
p->next=NULL;
return p;
}
p_cli_msg insert_head(p_cli_msg head,struct sockaddr_in cin,const char *name,int state)//头插函数
{
p_cli_msg s=create_node();
if(null(s))
return head;
strcpy(s->user_name,name);
s->state=state;
s->cin=cin;
s->next=head;
head=s;
return head;
}
int main(int argc, char const *argv[])
{
//创建套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("");
return -1;
}
printf("sfd=%d\n",sfd);
//将端口号快速重用函数
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(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
//定义一个存储消息的容器
Msg msg_buf;
msg_buf.message_type=-1;//初始化一下消息类型
//定义一个存放客户端地址信息的结构体
p_cli_msg Cli_addr_head=NULL;
//用来存储cin大小
struct sockaddr_in cin;
int cin_len=sizeof(cin);
//准备一个文件描述符容器
fd_set readfds,tempfds;
//清空容器
FD_ZERO(&readfds);
//将要监测的文件描述符放入集合
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
while(1)
{
tempfds = readfds;
//使用select函数对容器中的文件描述符进行赋值
int res = select(sfd+1,&tempfds,0,0,0);
if(res==-1)
{
perror("select error");
return -1;
}
else if(res==0)
{
printf("timeout\n");
return -1;
}
for(int fd=0;fd<=sfd;fd++)
{
if(!FD_ISSET(fd,&tempfds))
{
continue;
}
if(fd==0)
{
char wbuf[128]="";
scanf("%s",wbuf);
printf("触发了终端输入事件---\n");
if(strcmp(wbuf,"quit")==0)
{
strcpy(msg_buf.user_name,"--system--");
strcpy(msg_buf.text,"已退出");
p_cli_msg p=Cli_addr_head;
while(p!=NULL)
{
if(p->state==1)
{
sendto(sfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
}
p=p->next;
}
goto END;
}
//将消息发送给所有客户端
strcpy(msg_buf.text,wbuf);
strcpy(msg_buf.user_name,"--system--");
p_cli_msg p=Cli_addr_head;
while(p!=NULL)
{
if(p->state==1)
{
sendto(sfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
}
p=p->next;
}
}
if(fd==sfd)
{
//bzero(recv_buf->data.text,sizeof(recv_buf->data.text));
//读取信息结构体,并将客户端地址信息结构体存储到cin中
recvfrom(fd,&(msg_buf),sizeof(msg_buf),0,(struct sockaddr*)&(cin),&cin_len);
if(msg_buf.message_type==0)
{
Cli_addr_head=insert_head(Cli_addr_head,cin,msg_buf.user_name,1);
p_cli_msg p=Cli_addr_head;
strcpy(msg_buf.text,"已上线");
while(p!=NULL)
{
if(p->state==1)
{
sendto(fd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
}
p=p->next;
}
printf("[%s]已上线\n",msg_buf.user_name);
}
else if(msg_buf.message_type==2)
{
p_cli_msg p=Cli_addr_head;
strcpy(msg_buf.text,"已下线");
while(p!=NULL)
{
if(p->user_name==msg_buf.user_name)
{
p->state=0;
break;
}
}
p=Cli_addr_head;
while(p!=NULL)
{
if(p->state==1)
{
sendto(fd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
}
p=p->next;
}
}
else if(msg_buf.message_type==1)
{
p_cli_msg p=Cli_addr_head;
while(p!=NULL)
{
if(p->state==1)
{
sendto(fd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
}
p=p->next;
}
}
}
}
}
END:
close(sfd);
return 0;
}
客户端
#include <myhead.h>
#define SER_PORT 8080
#define SER_IP "10.102.145.16"
/*
#define SER_IP "192.168.122.153"
*/
#define LOGIN 0; //表示登录消息
#define CHAT 1; //表示聊天消息
#define LOGOUT 2; //表示退出消息
//定义关于客户端发送信息的结构体
typedef struct M{
int message_type; //表示所发送的消息类型
char user_name[20]; //表示客户端的用户名
char text[128]; //用来存储客户端所发送的消息
}Msg,*pMsg;
int main(int argc, char const *argv[])
{
Msg msg_buf;
char local_user_name[20];
printf("请输入用户名>>>");
scanf("%s",local_user_name);
strcpy(msg_buf.user_name,local_user_name);
msg_buf.message_type=-1;//初始化一下消息类型
//创建套接字
int cfd=socket(AF_INET,SOCK_DGRAM,0);
if(cfd==-1)
{
perror("");
return -1;
}
printf("cfd=%d\n",cfd);
//绑定
/*
struct sockaddr_in cin;
cin.sin_family=AF_INET;
cin.sin_port=htons(SER_PORT);
cin.sin_addr.s_addr=inet_addr(SER_IP);
*/
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);
int ser_len=sizeof(sin);
//准备一个文件描述符容器
fd_set readfds,tempfds;
//清空容器
FD_ZERO(&readfds);
//将要监测的文件描述符放入集合
FD_SET(0,&readfds);
FD_SET(cfd,&readfds);
//4.收发数据
msg_buf.message_type=0;
sendto(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,sizeof(sin));
puts("aaa");
while (1)
{
tempfds = readfds;
//使用select函数对容器中的文件描述符进行赋值
int res = select(cfd+1,&tempfds,0,0,0);
if(res==-1)
{
perror("select error");
return -1;
}
else if(res==0)
{
printf("timeout\n");
return -1;
}
for(int fd=0;fd<=cfd;fd++)
{
if(!FD_ISSET(fd,&tempfds))
{
continue;
}
if(fd==cfd)
{
//接收服务器发送的消息
recvfrom(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,&ser_len);
//自己的消息就不要打印出来了
if(strcmp(msg_buf.user_name,local_user_name)!=0)
{
printf("[%s]:%s\n",msg_buf.user_name,msg_buf.text);
}
}
else if(fd==0)
{
//因为收服务器发来的消息时,用户名会发生改变,所以我们先重置一下用户名
strcpy(msg_buf.user_name,local_user_name);
printf("请输入>>>");
fgets(msg_buf.text,sizeof(msg_buf.text),stdin);
msg_buf.text[strlen(msg_buf.text)-1]='\0';
//发送给服务器
msg_buf.message_type=1;
if (strcmp(msg_buf.text,"quit")==0)
{
msg_buf.message_type=2;
sendto(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,ser_len);
goto END;
}
sendto(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,ser_len);
}
}
}
END:
close(cfd);
return 0;
}
不过,这段代码还是有些许问题,改进中。。。