华清远见嵌入式学习——网络编程——小项目

项目要求:

代码实现:

服务器端:

#include <myhead.h>

//定义协议包
struct proto
{
	char type;
	char name[20];
	char text[128];	
};


int main(int argc, const char *argv[])
{
	//判断从终端输入的字符串的个数
	if(argc != 3)
	{
		printf("input error\n");
		printf("usage:./a.out 本机IP 本机端口\n");
		return -1;
	}

	//创建用于通信的套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd == -1)
	{
		perror("socket error");
		return -1;
	}

	//设置端口号快速重用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
	{
		perror("setsockopt error");
		return -1;
	}

	//绑定服务器IP和端口号
	填充服务器地址信息结构体
	short port = (short)atoi(argv[2]);
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr(argv[1]);
	sin.sin_port = htons(port);

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

	//定义客户端地址信息结构体
	struct sockaddr_in cin;
	cin.sin_family = AF_INET;

	socklen_t socklen = sizeof(cin);

	//定义客户端地址信息结构体数组,用于存放多个客户端地址信息
	struct sockaddr_in savecin[1024];

	//初始化每个信息结构体内的第一个成员
	for(int i = 0;i < 1024;i++)
	{
		savecin[i].sin_family = AF_INET;
	}

	定义一个用于检测文件描述符的集合
	fd_set readfds, tempfds;                          //在栈区定义

	清空容器中的内容
	FD_ZERO(&readfds);
	将要检测的文件描述符放入集合中
	FD_SET(sfd, &readfds);           //将sfd文件描述符放入
	FD_SET(0, &readfds);             //将0号文件描述符放入

	//对客户端的数据进行保存和转发

	char buf[256] = "";
	int res1,res2;
	int n = 0;

	//定义协议包结构体变量
	struct proto pro;
	struct proto send;
	
	//定义客户端地址信息结构体数组,用于存放多个客户端地址信息

	while(1)
	{
		bzero(buf,sizeof(buf));

		tempfds = readfds;

		使用select阻塞等待集合中的文件描述符有事件产生
		res1 = select(sfd+1, &tempfds, NULL, NULL, NULL);
		if(res1 == -1)
		{
			perror("select error");
			return -1;
		}else if(res1 == 0)
		{
			printf("time out\n");
			return -1;
		}


		//接收客户端信息
		if(FD_ISSET(sfd,&tempfds))
		{
			res2 = recvfrom(sfd,&pro,sizeof(pro),0,(struct sockaddr *)&cin,&socklen);
			if(res2 == -1)
			{
				perror("recvfrom error");
				return -1;
			}

			//登录时存储客户端到数组中
			if(pro.type == 'L')
			{
				savecin[n] = cin;
				n++;

				sprintf(buf,"---%s已上线---",pro.name);

				printf("%s\n",buf);
				for(int i = 0;i < n;i++)
				{
					if(savecin[i].sin_port == cin.sin_port)
					{
						continue;
					}	
					sendto(sfd,&pro,sizeof(pro),0,(struct sockaddr *)&savecin[i],sizeof(savecin[i]));

				}


			}


			if(pro.type == 'C')
			{
					//群聊
					send.type = pro.type;
					strcpy(send.name,pro.name);
					strcpy(send.text,pro.text);

					for(int i = 0;i < n;i++)
					{
						if(savecin[i].sin_port == cin.sin_port)
						{
							continue;
						}	
						sendto(sfd,&send,sizeof(send),0,(struct sockaddr *)&savecin[i],sizeof(savecin[i]));

					}

			}
			if(pro.type == 'Q')
			{
				//下线
				send.type = pro.type;
				strcpy(send.name,pro.name);
				strcpy(send.text,pro.text);

				for(int i = 0;i < n;i++)
				{
					bzero(buf,sizeof(buf));

					sprintf(buf,"---%s已下线---\n",send.name);

					printf("%s\n",buf);



					//删除该用户
					if(savecin[i].sin_port == cin.sin_port)
					{
						int t = i;
						for(int j = i;j <= n;j++)
						{
							savecin[t] = savecin[t+1];
							t++;		
						}
					}
					n--;
					sendto(sfd,&send,sizeof(send),0,(struct sockaddr *)&savecin[i],sizeof(savecin[i]));

				}
			}
		}
		if(FD_ISSET(0,&tempfds))
		{
			bzero(send.name,sizeof(send.name));
			bzero(send.text,sizeof(send.text));

			send.type = 'C';
			strcpy(send.name,"系统消息");

			fgets(send.text,sizeof(send.text),stdin);
			send.text[strlen(send.text)-1] = '\0';

			for(int i = 0;i <= n;i++)
			{


				sendto(sfd,&send,sizeof(send),0,(struct sockaddr *)&savecin[i],sizeof(savecin[i]));
			}	
		}
	}
	//关闭套接字文件描述符
	close(sfd);
	return 0;
}

客户端:

#include <myhead.h>

//定义协议包结构体
struct proto
{
	char type;
	char name[20];
	char text[128];
};

int main(int argc, const char *argv[])
{
	//判断终端输入的字符串的个数
	if(argc != 3)
	{
		printf("input error\n");
		printf("usage:./a.out 服务器IP 服务器端口号\n");
		return -1;
	}

	//创建用于通信的套接字
	int cfd = socket(AF_INET,SOCK_DGRAM,0);
	if(cfd == -1)
	{
		perror("socket error");
		return -1;
	}

	//填充服务器地址信息结构体
	short port = (short)atoi(argv[2]);	
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr(argv[1]);
	sin.sin_port = htons(port);
	socklen_t socklen = sizeof(sin);

	//定义协议包结构体变量
	struct proto pro;

	//创建接收消息用的协议包结构体变量
	struct proto in;


	//填充登录协议

	printf("请输入姓名>>");

	//登录
	pro.type = 'L'; 

	fgets(pro.name,sizeof(pro.name),stdin);
	pro.name[strlen(pro.name)-1] = '\0';

	sendto(cfd,&pro,sizeof(pro),0,(struct sockaddr *)&sin,sizeof(sin));

	定义一个用于检测文件描述符的集合
	fd_set readfds, tempfds;                          //在栈区定义

	清空容器中的内容
	FD_ZERO(&readfds);
	将要检测的文件描述符放入集合中
	FD_SET(cfd, &readfds);           //将sfd文件描述符放入
	FD_SET(0, &readfds);             //将0号文件描述符放入

	//向服务器发送或从服务器接收消息
	char rbuf[128] = "";
	int res = 0;
	char name1[20] = "";
	strcpy(name1,pro.name);


	while(1)
	{

		将集合内容复制一份
		tempfds = readfds;

		使用select阻塞等待集合中的文件描述符有事件产生
		res = select(cfd+1, &tempfds, NULL, NULL, NULL);
		if(res == -1)
		{
			perror("select error");
			return -1;
		}else if(res == 0)
		{
			printf("time out\n");
			return -1;
		}

		//群聊和退出
		if(FD_ISSET(0,&tempfds))
		{
			bzero(pro.text,sizeof(pro.text));
			
			//从终端获取内容
			fgets(pro.text,sizeof(pro.text),stdin);
			pro.text[strlen(pro.text)-1] = '\0';

			if(strcmp(pro.text,"quit") == 0)
			{
				//退出
				pro.type = 'Q';
				sendto(cfd,&pro,sizeof(pro),0,(struct sockaddr *)&sin,sizeof(sin));

				//关闭套接字文件描述符
				close(cfd);


				break;
			}else
			{
				//群聊
				pro.type = 'C';
				sendto(cfd,&pro,sizeof(pro),0,(struct sockaddr *)&sin,sizeof(sin));

			}
		}

		//接收来自服务器的消息
		if(FD_ISSET(cfd,&tempfds))
		{

			bzero(in.text,sizeof(in.text));

			res = recvfrom(cfd,&in,sizeof(in),0,NULL,NULL);
			if(res < 0)
			{
				perror("recvfrom error");
				return -1;
			}

			if(in.type == 'L')
			{
				printf("---%s已登录---\n",in.name);
			}
			if(in.type == 'C')
			{
				printf("%s:%s\n",in.name,in.text);
			}
			if(in.type == 'Q')
			{
				printf("---%s已下线---\n",in.name);
			}
		}


	}
	//关闭套接字文件描述符
	close(cfd);

	return 0;
}

代码运行效果图:

### 清远嵌入式学习路径与课程设置 #### 一、基础预备阶段 对于跨专业的学生来说,进入正式的嵌入式开发培训之前,建议先自学一些基础知识,包括但不限于C语言、数据结构、计算机组成原理、计算机网络以及操作系统等内容[^1]。这些前置技能有助于更好地理解后续更复杂的概念和技术。 #### 二、初级入门阶段 在这个阶段,重点放在夯实C语言基础上面,因为这是整个嵌入式软件开发的核心工具之一。学员应该确保自己能够熟练运用各种语法特性,并且具备一定的算法思维能力。同时也要熟悉基本硬件接口操作方法,比如GPIO控制等。此过程中有任何不明白之处都应及时向指导教师寻求帮助直至彻底搞清楚为止。 #### 三、中级深化阶段 当掌握了初步的知识体系之后,则需进一步探索更为深入的主题领域——单片机编程及其周边组件(如RTOS, U-Boot)。此时可以通过参与具体的工程项目练习来巩固所学到的东西;例如基于STM32系列MCU构建简单的控制系统或是实现特定功能模块的设计与调试工作[^3]。 #### 四、高级实战训练 最后,在积累了足够的理论认知和实践经验的基础上,就可以着手准备综合性更强的任务了。这类活动通常涉及多个学科交叉融合的应用场景,像智能交通管理系统里的车辆检测装置研发、物联网平台下智能家居设备互联互通解决方案制定等等。通过此类高强度的真实案例演练不仅能让参与者全面检验自己的技术水平,更能培养解决问题的能力和团队协作精神[^4]。 #### 五、职业发展导向 随着技术栈逐渐完善成熟起来以后,还可以考虑专攻某些细分方向上的高阶课题研究,诸如实时多任务调度机制优化、低功耗无线传感网协议栈定制化开发之类的技术难题攻关项目。另外也不妨关注行业动态趋势变化情况,积极参加各类专业技术交流会议论坛等活动,以便及时获取最新资讯情报支持个人职业生涯长远规划与发展目标设定[^5]。 ```mermaid graph TD; A(开始) --> B{是否有相关背景?}; B -- 是 --> C[直接进入下一环节]; B -- 否 --> D[C语言 & 数据结构<br> 计算机组成原理 <br> 操作系统 <br> 计算机网络]; D --> E(C语言强化); E --> F[单片机/RTOS/U-Boot]; F --> G[综合项目实践]; G --> H[职业发展方向选择]; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值