思维导图

嵌入式学习-网络编程-Day6-LMLPHP

作业

使用UDP实现一个多人简易聊天室

服务器

#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;
}

不过,这段代码还是有些许问题,改进中。。。

01-26 06:23