Linux 基本语句_16_Udp网络聊天室

这篇具有很好参考价值的文章主要介绍了Linux 基本语句_16_Udp网络聊天室。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

代码:

服务端代码:

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define N 128
#define L 1
#define C 2
#define Q 3

typedef struct{
	int type;
	char name[N];
	char text[N];
}MSG; // 存信息 

typedef struct node{
	struct sockaddr_in addr; // 存ip 和 端口号 
	struct node *next; // 链表 
}linklist_t;

linklist_t *linklist_create(); // 创建链表函数 

void do_login(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr); // 某客端上线,将数据发送给其他在线客户端 
void do_chat(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr); // 将用户想要发送的数据广播给其他用户 
void do_quit(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr); // 在链表中删除自己的记录,并将自己退出的信息发送給其他客户端 

int main(int argc, const char *argv[]){
	int sockfd;
	struct sockaddr_in serveraddr, clientaddr; // 储存信息 
	socklen_t addrlen = sizeof(serveraddr);
	
	if(argc < 3){
		printf("argc number error\n");
		return -1;
	}
	
	/* 创建套接字 */ 
	if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ // IPV4、UDP协议、协议标志 
		printf("socket error\n");
		return -1;
	}
	
	/* 填充服务器网络信息 */ 
	serveraddr.sin_family = AF_INET; 
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	 
	if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0){ // 套接字与服务器网络信息绑定、(套接字是中转站,bind将ip和端口信息存入中转站)
		printf("bind error\n");
		return -1;
	}
	
	MSG msg;
	
	pid_t pid;
	
	if((pid = fork()) < 0){
		printf("fork error\n");
		return -1;
	}
	else if(pid == 0){ // 子进程 
		msg.type = C;
		strcpy(msg.name, "server");
		
		while(1){
			fgets(msg.text, N, stdin); // 等待控制台输入
			msg.text[strlen(msg.text) - 1] = '\0';
			
			sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, addrlen); // 子进程和父进程绑定在同一个ip地址 和 端口号 子进程能向父进程发送给对方 
		} 
	}
	else{ // 父进程负责接收数据并处理 
	    linklist_t *h = linklist_create();
		
		while(1){
			recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &addrlen); // 从中转站接收数据直到有数据为止 
			printf("%d -- %s -- %s\n", msg.type, msg.name, msg.text); // 打印接收的数据 
			
			switch(msg.type){ // 根据数据的类型做不同操作 
				case L:
					do_login(msg, h, sockfd, clientaddr); // 登录广播提醒 
					break;
				case C:
					do_chat(msg, h, sockfd, clientaddr); // 广播聊天 
					break;
				case Q:
					do_quit(msg, h, sockfd, clientaddr); // 广播退出 
					break;
			}
		}   
	}
	return 0; 
}

linklist_t *linklist_create(){ // 创建链表 
	linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t)); // 创建一个链表节点,h为链表头部 
	h->next = NULL; // 整个链表只有一个节点 
	
	return h; 
}

void do_login(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr){
	linklist_t *p = h;
	
	/* 用户登录信息发送给其他客户 */ 
	sprintf(msg.text, "-------- %s login -------------", msg.name);
	while(p->next != NULL){
		sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->next->addr, sizeof(struct sockaddr_in)); // 发送那个给链表中所有对象 
		p = p->next;
	}
	
	linklist_t *temp = (linklist_t *) malloc(sizeof(linklist_t)); // 创建一个新结点 
	
	temp->addr = clientaddr; // 客户端信息存入结点 
	temp->next = h->next;
	h->next = temp; // 将temp存入链表末尾 
	
	return;
}

void do_chat(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr){
	
	char buf[N] = {};
	linklist_t *p = h;
	
	/* 将用户信息发送给其他在线的用户 */
	sprintf(buf, "%s : %s", msg.name, msg.text);
	strcpy(msg.text, buf); // 将数据存入msg 
	
	while(p->next != NULL){ // 发送数据 
		if(memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) == 0){ // 数据是自己的就不传输了 
			p = p->next;
		} 
	    else{ // 其他人就发送 
		    sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->next->addr, sizeof(struct sockaddr_in)); // 发送的数据、长度、发送的位置 
		    p = p->next;
	    }
    }
	return; 
}

void do_quit(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr){
	linklist_t *p = h;
	linklist_t *temp;
	
	/* 将用户退出的信息发送给其他用户,并将其信息从链表中删除 */
	
	sprintf(msg.text, "-------- %s offline --------", msg.name);
	
	while(p->next != NULL){
		if(memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) == 0){ // 自己的就不发送 
			temp = p->next;
			p->next = temp->next;
			free(temp); // 释放本结点 
			temp = NULL; // 指针至为空指针 
		}
		else{
			sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->next->addr, sizeof(struct sockaddr_in)); // 不是自己就发送 
			p = p->next;
		}
	}
	return; 
}

客户端代码:

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <strings.h>

#define N 128
#define L 1
#define C 2
#define Q 3

typedef struct{
	int type;
	char name[N];
	char text[N];
}MSG; // 存信息 



int main(int argc, const char *argv[]){
	int sockfd;
	struct sockaddr_in serveraddr, clientaddr; // 储存信息 
	socklen_t addrlen = sizeof(serveraddr);
	
	if(argc < 3){
		printf("argc number error\n");
		return -1;
	}
	
	/* 创建套接字 */ 
	if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ // IPV4、UDP协议、协议标志 
		printf("socket error\n");
		return -1;
	}
	
	/* 填充服务器网络信息 */ 
	serveraddr.sin_family = AF_INET; 
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	
	MSG msg;
	
	msg.type = L;
	
	printf("please enter your name: ");
	fgets(msg.name, N, stdin); // 将控制台输入的信息传入name中
	msg.name[strlen(msg.name) - 1] = '\0'; 
	sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, addrlen); // 将数据发送给服务端 
	
	pid_t pid;
	
	if((pid = fork()) < 0){
		printf("fork error\n");
		return -1;
	}
	else if(pid == 0){ // 子进程,发送数据 
		
		while(1){
			fgets(msg.text, N, stdin); // 等待控制台输入
			msg.text[strlen(msg.text) - 1] = '\0';
			
			if(strncmp(msg.text, "quit", 4) == 0){ // 若要退出 
				msg.type = Q; // 退出广播 
			    sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, addrlen); // 子进程和父进程绑定在同一个ip地址 和 端口号 子进程能向父进程发送给对方
				close(sockfd);
				kill(getppid(), SIGKILL); // 退出父进程 
				return 0; 	
			} 
			
			msg.type = C; // 聊天 
			sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, addrlen); // 子进程和父进程绑定在同一个ip地址 和 端口号 子进程能向父进程发送给对方
		} 
	}
	else{ // 父进程负责接收数据

		
		while(1){
			recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, &addrlen); // 从中转站接收数据直到有数据为止 
			printf("%s\n", msg.text); // 打印接收的数据 
			
		}   
	}
	return 0; 
}

效果:

Linux 基本语句_16_Udp网络聊天室,网络,linux,udp
总体效果是客户上线状态、退出状态、发送的消息都能通过广播,将信息发送给所有在线客户端,服务端能接收并显示所有客户端发送的消息,且也具备广播能力

原理:

服务端:

服务端创建了一个链表,这个链表中的每个节点是专用于储存客户端的ip地址和端口号等一系列信息,目的是方便遍历实现广播功能

服务端的创建了父子进程,子进程专门接收控制台发送的数据,并转发给父进程,父进程将数据广播给所有客户端,朴素的讲子进程接收控制台数据,父进程接收客户端信息并广播

客户端:

客户端也是创建父子进程,父进程负责接收服务器转发的数据,并打印。子进程负责发送,从本控制台获取的信息并发送给服务端通过服务器广播给其他客户端

服务器本质就是中转站,负责接收客户端信息状态并广播

拓展:

套接字:

套接字可以理解为网络通信的中转站,将通信双方的ip地址和端口号等相关信息存入套接字,以便通信双方能通过ip地址和端口号找到对应接收端。

服务端和客户端都创建套接字的原因:

在一个典型的客户端-服务器模型中,服务器和客户端通过套接字建立通信。一般情况下,服务器会先创建一个套接字并绑定到一个特定的 IP 地址和端口上,然后等待客户端连接。
在客户端与服务器建立连接时,客户端会创建一个新的套接字,并尝试连接到服务器的套接字地址。如果连接成功,服务器会接受这个连接并为客户端创建一个新的套接字,该套接字将用于与这个特定客户端之间的通信。
这样,服务器会保持一个主套接字用于监听客户端的连接请求,并为每个连接创建一个新的套接字来处理与特定客户端之间的通信。这些连接的套接字通常是独立的,即服务器和每个客户端之间都有一个独立的套接字,用于他们之间的通信。

UDP连接方式:

在 UDP 协议中,客户端并不需要显式地调用 bind() 来绑定一个端口。通常情况下,在客户端发送数据时,系统会自动分配一个临时的端口号,并在发送数据时使用这个端口号。这个临时端口号通常在发送后被释放,因此客户端不需要显式地绑定一个端口。
客户端在发送数据时,使用 sendto() 或者 sendmsg() 等函数向目标服务器发送数据报。在发送时,指定目标服务器的 IP 地址和端口号即可,而不需要调用 bind() 来指定客户端的本地端口。UDP是无连接的,因此客户端不需要事先建立连接,只需要在发送数据时指定目标地址和端口即可。
相反,服务器通常会先调用 bind() 来绑定一个固定的端口号,以便监听客户端发送来的数据。服务器需要绑定一个固定端口号来等待客户端的连接请求或者接收数据报。
总之,在UDP中,客户端通常不需要显式地调用 bind() 来绑定端口,它可以自动分配一个临时的端口来发送数据。服务器端则需要绑定一个固定的端口号来等待客户端的连接或接收数据。

区别:

TCP是面向连接的协议,它在通信之前需要建立连接,并确保数据传输的可靠性。它提供数据的可靠性保证、流量控制和拥塞控制。
UDP是无连接的协议,不需要在发送数据之前建立连接。它不保证数据的可靠性,也不提供类似TCP的可靠性保证机制。文章来源地址https://www.toymoban.com/news/detail-766060.html

到了这里,关于Linux 基本语句_16_Udp网络聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机网络 TCP/UDP程序开发网络聊天室

    TCP/UDP程序开发 开发TCP/UDP协议应用程序,掌握网络应用程序的工作原理。通过该实验,深入理解UDP和TCP协议的异同点,了解网络协议的工作过程,学会网络通信编程的基本方法,能够编制网络应用程序。 (1)了解和掌握“基于UDP-面向无连接的应用程序/基于TCP-面向连接的

    2024年02月05日
    浏览(89)
  • 【网络编程】UDP简单实现翻译软件与网络聊天室

    在上一章【网络编程】demo版UDP网络服务器实现实现了客户端和服务端之间的数据的发送与接收,上一章我们是直接让服务端把接收到的数据打印出来。 但是服务端并不是只接收到数据就完了,它还要 处理任务 。 所以我们可以在服务端设置一个回调函数: 用来处理接收到的

    2024年02月05日
    浏览(80)
  • 【嵌入式学习】网络通信基础-项目篇:简单UDP聊天室

    源码已在GitHub开源:0clock/LearnEmbed-projects/chat 客户端功能: 上线发送登录的用户名[yes] 发送消息和接收消息[yes] quit退出 服务器端功能: 统计用户上线信息,放入链表中[yes] 接收用户信息并给其他用户发送消息[yes] 服务器也支持给所有用户群发消息[yes] 接收下线提醒

    2024年01月25日
    浏览(64)
  • [Socket]Python用UDP协议建立带有私聊功能的网络聊天室-建立聊天工具

    前些天实习面试的时候被面试官问到Socket编程的问题,即“Socket创建和释放的具体过程是什么”,当时答不上来,似乎是涉及到发送和接收缓冲区的问题。由于自己在Socket编程这一块知识较为薄弱,于是写下这篇文章,当作复习下Socket编程知识。 首先,该实验是我在大三上学

    2024年02月03日
    浏览(48)
  • 【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

    源IP地址(Source IP Address): 源IP地址是数据包发送方(或数据流出发点)的唯一标识符。它用于在互联网或本地网络中定位发送数据包的设备或主机。源IP地址是数据包的出发点,即数据从这个地址开始传送,向目的IP地址指示的设备发送。 在TCP/IP协议中,源IP地址通常由发

    2024年02月14日
    浏览(86)
  • UDP套接字的通信(实现英汉互译/程序替换/多线程聊天室/Windows与Linux通信)

    我们在客户端发英文,服务端做翻译工作,让翻译好的中文再次发给我们的客户端,然后打印出来。 翻译的操作 创建一个txt文件里面包含英汉互译的数据 dict.txt 对txt中的数据进行操作 分割函数 将英汉通过冒号分开。 将文件数据插入map里面 重新加载文件 通过捕捉2号(ctrl

    2024年02月11日
    浏览(38)
  • UDP聊天室

    1.头文件 2.服务器 3.客户端 4.makefile 5.结果

    2024年02月10日
    浏览(48)
  • 多人聊天室(带私聊功能)Linux网络编程基础

    在和同学一起努力下终于完成了期末作业哈哈哈哈 文章目录 目录 前言 一、需求分析 二、功能设计 1.服务器端: 2.客户端: 三、流程图: 编程流程图: 服务器流程图: 客户端流程图: 四、运行效果: 项目源码: 服务器源码 客户端源码: 总结: Linux网络编程是我们这学

    2024年02月09日
    浏览(59)
  • Python多人聊天室-基于socket UDP协议

    使用Python编写的基于socket UDP通信的多功能即时聊天室,包含Tkinter编写的图形化聊天界面,功能包括有账号注册和登录,登录成功后可以查看在线用户,并和聊天室内的其他在线用户聊天,包含私聊和群发,能发送文字、表情包,以及文件等。 登录和注册 显示在线用户 群聊

    2024年02月11日
    浏览(58)
  • C语言实现--基于UDP的多人在线聊天室

    目录 实现功能 实现思想 实现代码(部分及详解) 服务端部分代码 客户端部分代码 实现效果 项目中出现的问题和解决方法 项目整体代码展示 代码优化思路 服务端代码 客户端代码 服务端可以同时连接多个客户端; 新的客户端连接服务端时,可以在服务端显示自己的名字并

    2024年02月04日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包