基于UDP的网络多人聊天室

UDP服务器

复制代码
#include <myheader.h>

//宏定义打印错误信息
#define PRINT_ERR(msg)                                  \
	do                                                  \
	{                                                   \
		printf("%S,%D,%S\n",__FILE__,__LINE__,__func__);\
		perror(msg);                                    \
		exit(-1);                                       \
	}while(0);

//定义一个结构体
//由于客户端给服务器发送第数据内容较多,定义一个结构体来发送
typedef struct
{
	char code;  //操作码 'L'登录 'C'群聊 'Q'退出
	char name[32];   //保存登录用户名
	char txt[128];   //保存发送的信息
}msg_t;

//定义一个链表
// 给在线所有客户端发送数据,将每一个客户端的信息用链表来保存
typedef struct NODE
{
	struct sockaddr_in c_addr;   //数据域  客户端第网络信息结构体
	struct NODE *next;   // 指针域   保存下一个结点的地址
}node_t;

//创建一个链表头第函数,定义链表头结点
//链表头结点函数
void creat_link(node_t **head)
{
	*head = (node_t *)malloc(sizeof(node_t));
}

int do_register(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead)
{
	//遍历链表将登录信息发送给所以人
    node_t *p = phead;
    while (p->next != NULL)
    {
        p = p->next;
        if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1)
        {
            perror("recvfrom error");
        }
    }
    //将登录的客户端信息插入保存在链表
    //头插
    //定义一个新的指针保存客户端信息
    node_t *newp = NULL;
    creat_link(&newp);
    newp->c_addr = clientaddr;
    newp->next = phead->next;
    phead->next = newp;
    return 0;
}

int do_group_chat(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead)
{
	//遍历链表,将消息发给除自己之外的所有人
    node_t *p = phead;
    while (p->next != NULL)
    {
        p = p->next;
        //判断链表客户端信息是否是自己
        //是自己就不发送
        if (memcmp(&(p->c_addr), &clientaddr, sizeof(clientaddr)))
        {
            if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1)
            {
                perror("recvfrom error");
            }
        }
    }
    return 0;
}

//退出群聊操作
int quit_group_chat(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead)
{
	node_t *p = phead;

    while (p->next != NULL)
    {
        //判断链表客户端信息是否是自己
        //是自己就不发送并且将自己的客户端信息在链表内删除
        if (memcmp(&(p->next->c_addr), &clientaddr, sizeof(clientaddr)))
        {
            p = p->next;
            if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1)
            {
                perror("recvfrom error");
            }
        }
        else
        {
            node_t *pnew;
            pnew = p->next;
            p->next = pnew->next;
            pnew->next = NULL;
            free(pnew);
            pnew = NULL;
        }
    }
    return 0;
}

int main(int argc, const char *argv[])
{
	//入参合理性判断
	if(argc != 3)
	{
		printf("age:%s ip port\n",argv[0]);
		return -1;
	}

	//创建套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd == -1)
	{
		perror("socket error");
		return -1;
	}
	printf("sfd = %d\n",sfd);

	//创建服务器网络信息结构体
	struct sockaddr_in sin;
	memset(&sin,0,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(atoi(argv[2]));
	sin.sin_addr.s_addr = inet_addr(argv[1]);
	socklen_t sin_len = sizeof(sin);

	//绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1)
	{
		perror("bind error");
		return -1;
	}
	printf("bind success\n");

	//创建客户端的网络信息结构体
	struct sockaddr_in cin;
	memset(&cin,0,sizeof(cin));
	socklen_t cin_len = sizeof(cin);
	msg_t msg;

	//建一个父子进程 
	//用来实现服务器既可以发送系统消息,又可以接收客户端的信息
	 pid_t pid;
	 pid = fork();
	 if(pid == -1)
	 {
		 //创建错误
		 perror("fork error");
	 }
	 else if(pid == 0)
	 {
		 //子进程
		 //接收数据并处理
		 //循环接收客户端发来的信息,通过Switch判断code所存的协议
		 //定义链表头结点
		 node_t *phead = NULL;
		 creat_link(&phead);
		 phead->next = NULL;
			

		 //循环接受客户端发来的信息并通过switch进行判断执行哪个功能函数
		 while (1)
		 {
			 memset(&msg, 0, sizeof(msg));//清空操作
			 memset(&cin, 0, sizeof(cin));//清空操作
			 if ((recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, &cin_len)) == -1)
			 {
				 perror("recvfrom error");
			 }
			 printf("%8s : [%s]\n", msg.name, msg.txt);
			 switch (msg.code)
			 {
			 case 'L':
				 do_register(sfd, msg, cin, phead);
				 break;
			 case 'C':
				 do_group_chat(sfd, msg, cin, phead);
				 break;
			 case 'Q':
				 quit_group_chat(sfd, msg, cin, phead);
				 break;
			 }
		 }
	 }
	 else if(pid > 0)
	 {

		 //父进程
		 //发送系统信息
		 //视为一个客户端,向子进程发送消息给在线客户
		 msg.code='C';
		 strcpy(msg.name,"server");
		 while(1)
		 {
			 fgets(msg.txt,128,stdin);
			 msg.txt[strlen(msg.txt)-1]='\0';
			 if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr *)&sin,sin_len)==-1)
			 {
				 perror("sendto error");
			 }
		 }
		close(sfd);
		return 0;
	 }
	return 0;
}

UDP客户端

复制代码
#include <myheader.h>

//宏定义打印错误信息
#define perror(msg)                                      \
    do                                                      \
    {                                                       \
        printf("%s,%d,%s\n", __FILE__, __LINE__, __func__); \
        perror(msg);                                        \
        exit(-1);                                           \
    } while (0)

typedef struct
{
    char code; //操作码 'L' 登录  'C' 群聊  'Q' 退出
    char name[32];
    char txt[128];
} msg_t;

int main(int argc, const char *argv[])
{
	//入参合理性判断
    if (argc != 3)
    {
        printf("age:%s ip port\n", argv[0]);
        return -1;
    }
    //创建套接字
    int sfd;
    if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket error");
    }
    //创建服务器网络信息结构体
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    sin.sin_port = htons(atoi(argv[2]));
    socklen_t sin_len = sizeof(sin);
    //给服务器发送登录数据包
    msg_t msg;
    memset(&msg, 0, sizeof(msg_t));
    msg.code = 'L';
    printf("请输入用户名:");
    fgets(msg.name, 32, stdin);
    msg.name[strlen(msg.name) - 1] = '\0';

    strcpy(msg.txt, "加入群聊");
    if (sendto(sfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&sin, sin_len) == -1)
    {
        perror("sendto error");
    }
    //创建父子进程
    pid_t pid;
    pid = fork();
    if (pid == -1)
    {
        perror("fork error");
    }
	else if (pid == 0)
    {
        //子进程
        //接受数据并处理
        while (1)
        {
            //每次循环前将msg置零
            memset(&msg, 0, sizeof(msg));
            //接受服务器发过来的信息并打印到终端上
            if (recvfrom(sfd, &msg, sizeof(msg_t), 0, NULL, NULL) == -1)
            {
                perror("recvfrom error");
            }
            printf("%8s:[%s]\n", msg.name, msg.txt);
        }
    }
    else if (pid > 0)
    {
        //父进程
        //发送消息
        while (1)
        {   
            //memset会把name清除
            msg.code = 'C';
            fgets(msg.txt, 128, stdin);
            msg.txt[strlen(msg.txt) - 1] = '\0';
            if (strcmp(msg.txt, "quit") == 0)
            {
                msg.code = 'Q';
                strcpy(msg.txt, "退出群聊");
            }
            if (sendto(sfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&sin, sin_len) == -1)
            {
                perror("sendto error");
            }
            if (strcmp(msg.txt, "退出群聊") == 0)
            {
                break;
            }
        }
        kill(pid,SIGKILL);
        wait(NULL);
        close(sfd);
	}
	return 0;
}
相关推荐
沐浴露z28 分钟前
【深入理解计算机网络05】数据链路层:组帧,差错控制,流量控制与可靠传输
网络·计算机网络·网络编程·408
galaxylove44 分钟前
Gartner发布网络弹性指南:将业务影响评估(BIA)嵌入网络弹性策略的核心,重点保护基础设施和关键业务系统
网络·安全·web安全
未来之窗软件服务1 小时前
操作系统应用开发(二十五)RustDesk 502错误—东方仙盟筑基期
网络协议·远程桌面·仙盟创梦ide·东方仙盟·rustdek
山,离天三尺三1 小时前
基于LINUX平台使用C语言实现MQTT协议连接华为云平台(IOT)(网络编程)
linux·c语言·开发语言·网络·物联网·算法·华为云
爱隐身的官人4 小时前
JAVA代码审计总结
java·网络·安全
zwhy03114 小时前
TCP服务器设计思路
linux·服务器·网络
苜蓿草茸茸4 小时前
HTTP 协议详解
网络协议·http
飞天巨兽6 小时前
HTTP基础教程详解
前端·网络·网络协议·http
dot to one6 小时前
应用层:Http、Https
linux·c++·网络协议
岑梓铭6 小时前
计算机网络第四章(10)——网络层《路由算法+路由协议》
网络·笔记·计算机网络·考研·智能路由器·408