
课程设计成果
院(系):_电气与信息工程学院_ 班 级: 计科普0802
学生姓名: 学 号:
设计地点(单位)___ _I315 __________ _______
设计题目:_____ 聊天程序设计_________________________ _
完成日期: 2011 年 9 月 5 日
指导教师评语: _______________________________________
_________________________________________________________________________________________________________________________________________________________________________________________________________
成绩(五级记分制):______ __________
教师签名:_________________________
摘要
嵌入式linux在电子行业的应用很广泛,学习嵌入式linux显得非常重要。这次课程设计的主要目的是检验上学期学习linux后的效果。通过基础题的代码编写,熟悉linux C语言编程技巧。通过完成聊天程序的设计,熟悉linux底层编程。利用QT设计界面,缩短开发时间。
关键字:嵌入式linux;基础题;聊天程序;QT
课程设计任务书
设计题目:基础题目
| 学生姓名 | 庄桐泉 | |||
| 课程名称 | 嵌入式linux课程设计 | 专业班级 | 计科普2008 | |
| I315 | 起止时间 | 2011-8-29至2011-9-9 | ||
| 设计内容及要求 |
GCC编译器的使用,LINUX系统C程序设计编译、调试方法 1、编写程序将数组内容倒置a[]=”1234567”。 2、利用指针将数据A的内容复制到数据B。 3、创建两线程,通过打印输出各自线程号和打印次序,要求从打印结果看出两个线程是并发执行的。 4、创建两线程,A线程通过消息队列发消息,B线程收到后在屏幕打印输出,要求两线程个打印出线程号和消息内容。 5、创建两线程,A线程循环打印数组a[100],B线程循环将数组成员+1,要求利用互斥锁,使每次输出a[0]==a[99]. 6、创建两线程,A线程每2秒打印一次字母A,B线程每秒打印一次字母B,要求利用同步信号量,使输出字母B总是在A之后。 8、通过Makefile,将project中的一个.c编译成.a,另一个.c调用.a的函数,要求实现静态库的生成和调用,运行结果正确。 | |||
| 设计 参数 | ||||
| 进度 要求 | 第一周之内完成 | |||
| 参考资料 | 1、嵌入式Linux应用程序开发标准教程.人民邮电出版社。华清远见嵌入式培训中心。2010.7 2、ZLG ARMMAGIC2410实验指导、资料。 | |||
其它 | ||||
| 学生姓名 | 庄桐泉 | |||
| 课程名称 | 嵌入式linux课程设计 | 专业班级 | 计科普2008 | |
| I315 | 起止时间 | 2011-8-29至2011-9-9 | ||
| 设计内容及要求 |
1、在QT(或miniGUI)编写聊天程序,交叉编译后下载到目标机,可实现两台目标机可发送文本聊天 2、QT(或miniGUI)界面设计至少包括“发送”,“重置”两个按钮,要求必须把目标机的硬件键盘映射到QT设计的软件界面中,实现软件按钮的功能。 选作:发送文件。 | |||
| 设计 参数 | ||||
| 进度 要求 | 做完基础题目之后开始做本题目。 | |||
| 参考资料 | 1、嵌入式Linux应用程序开发标准教程.人民邮电出版社。华清远见嵌入式培训中心。2010.7 2、ZLG ARMMAGIC2410实验指导、资料。 | |||
其它 | ||||
| 说明 | 1.本表应在每次实施前一周由负责教师填写二份,学院审批后交学院教务办备案,一份由负责教师留用。2.若填写内容较多可另纸附后。3.一题多名学生共用的,在设计内容、参数、要求等方面应有所区别。 | |||
题目一 基础题
1 设计内容及要求
1.1 二小题
(1) 编写程序将数组内容倒置a[]=”1234567”。
(2) 程序代码实现如下:
char a[]="01234567"; ①
char tmp;
int i = 0,j = 0;
for(i = 0,j = strlen(a)-1;i<=strlen(a)/2-1;i++,j--) ②
{
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
for(i = 0;i printf("\\n"); (3)程序分析: ①定义数组 ②数组内容倒置 ③输出倒置后数组内容 (4)程序执行效果如下: 1.2三小题 (1)利用指针将数据A的内容复制到数据B。 (2)程序代码实现如下: char A[] = "abcd"; char B[] ="1234"; ① int i = 0; char *tmp; ② tmp = A; ③ for(i = 0;i for(i = 0;i printf("\\n"); (3)程序分析: ①定义数组 ②定义指针 ③指针指向数组A ④移动指针拷贝数字到B中 ⑤显示拷贝后的数组 (4)程序执行效果如下: 1.3四小题 (1)创建两线程,通过打印输出各自线程号和打印次序,要求从打印结果看出两个线程是并发执行的。 (2)程序代码如下: #define THREAD_NUMBER 2 #define REPEAT_NUMBER 5 #define DELAY_TIME_LEVELS 10.0 void * thrd_func(void *arg) ① { int thrd_num = (int)arg; int delay_time = 0; int count = 0; printf("Thread %d is starting\\n", thrd_num); for (count = 0; count < REPEAT_NUMBER; count++) { delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1; sleep(delay_time); ② printf("\Thread %d: job %d delay = %d\\n", thrd_num, count, delay_time); } printf("Thread %d finished\\n", thrd_num); pthread_exit(NULL); ③ } int main(void) { pthread_t thread[THREAD_NUMBER]; int no = 0, res; void * thrd_ret; for (no = 0; no < THREAD_NUMBER; no++) ④ { res = pthread_create(&thread[no], NULL, thrd_func, (void*)no); } for (no = 0; no < THREAD_NUMBER; no++) { res = pthread_join(thread[no], &thrd_ret); } return 0; } (3)程序分析: 程序中创建2个线程,为了更加方便地描述线程之间的并行执行,让2个线程重用一个执行函数。每个线程都有5次循环,每次循环之间会随机等待1-10s的时间,意义在于模拟每个任务的到达时间是随机的,更加形象看出线程之间的并行执行。 1创建线程执行函数,程序中2个线程重用一个执行函数。 2让线程延时,延时时间随机产生。 3线程退出。 4创建2个线程。 (4)程序执行效果如下: 1.3五小题 (1)创建两线程,A线程通过消息队列发消息,B线程收到后在屏幕打印输出,要求两线程个打印出线程号和消息内容。 (2)程序代码如下: #define BUFSZ 512 #define THREAD_NUMBER 2 struct message ① { long msg_type; char msg_text[BUFSZ]; }; struct message msg; int qid; /*Create queue*/ void * thrd_func0(void *arg) ② { int thrd_num = (int)arg; int len = 0; printf("Thread %d is starting\\n", thrd_num); sprintf(msg.msg_text,"%s msg.msg_type = getpid(); len = strlen(msg.msg_text); /*添加消息到消息队列*/ msgsnd(qid, &msg, len, 0); ③ printf("send the message is:%s\\n",(&msg)->msg_text); pthread_exit(NULL); ④ } void * thrd_func1(void *arg) ⑤ { int thrd_num = (int)arg; printf("Thread %d is starting\\n", thrd_num); /*读取消息队列*/ msgrcv(qid, &msg, BUFSZ, getpid(), 0); ⑥ printf("recv the message is:%s\\n",(&msg)->msg_text); pthread_exit(NULL); } void *(*const func[THREAD_NUMBER])(void *)={thrd_func0,thrd_func1}; int main(void) { pthread_t thread[THREAD_NUMBER]; int no = 0, res; void * thrd_ret; key_t key; int len; key = ftok(".", 'a'); ⑦ qid = msgget(key,IPC_CREAT|0666); ⑧ for (no = 0; no < THREAD_NUMBER; no++) { res = pthread_create(&thread[no], NULL, func[no], (void*)no); } for (no = 0; no < THREAD_NUMBER; no++) ⑨ { res = pthread_join(thread[no], &thrd_ret); } (msgctl(qid, IPC_RMID, NULL); ⑩ return 0; } (3) 代码分析如下: 该程序实现了使用消息队列进行线程之间的通信,包括消息队列的创建、消息发送和读取、消息队列的撤销和删除等操作。 1定义消息队列消息结构体 2创建线程0,用于消息队列发送消息 3添加消息到消息队列 4退出线程 5创建线程1,用于消息队列读取消息 6读取消息队列 7根据不同的路径和关键表示产生标准的key 8创建消息队列 9等待线程 10从系统内核中移走消息队列 (4)程序执行效果如下: 1.4六小题 (1)创建两线程,A线程循环打印数组a[100],B线程循环将数组成员+1,要求利用互斥锁,使每次输出a[0]==a[99]. (2)程序源代码如下: (3)程序分析如下: 1.5七小题 (1)创建两线程,A线程每2秒打印一次字母A,B线程每秒打印一次字母B,要求利用同步信号量,使输出字母B总是在A之后。 (2)程序代码如下: #define THREAD_NUMBER 2 sem_t sem[THREAD_NUMBER]; void * thrd_func0(void *arg) //A ① { arg = arg; while(1) { printf("A\\n"); ② sem_post(&sem[1]); ③ sleep(2); ④ } pthread_exit(NULL); ⑤ } void * thrd_func1(void *arg) //B ⑥ { arg = arg; while(1) { sem_wait(&sem[1]); ⑦ printf("B\\n"); ⑧ sleep(1); ⑨ } pthread_exit(NULL); } void *(*const func[THREAD_NUMBER])(void *)={thrd_func0,thrd_func1}; int main(void) { pthread_t thread[THREAD_NUMBER]; int no = 0, res; void * thrd_ret; for (no = 0; no < THREAD_NUMBER; no++) ⑩ { res = pthread_create(&thread[no], NULL, func[no], (void*)no); } printf("Create thread success\\n Waiting for threads to finish...\\n"); for (no = 0; no < THREAD_NUMBER; no++) { sem_destroy(&sem[no]); } return 0; } (3)程序分析如下: 程序创建两线程,实现A线程每2秒打印一次字母A,B线程每秒打印一次字母B,利用同步信号量,使输出字母B总是在A之后。 1创建线程0执行函数,用于打印字符’A’ 2打印字符’A’ 3对信号量1进行V操作 4延时2s 5退出线程 6创建线程1执行函数,用于打印字符’B’ 7对信号量1进行P操作 8打印字符’B’ 9延时1s 10创建线程 (4)程序执行效果如下: 1.6八小题 (1)通过Makefile将project中的一个.c编译成.a,另一个.c调用.a的函数,要求实现静态库的生成和调用,运行结果正确。 (2)编写静态库程序thread.c如下: #include void pf1(void) { printf("********\\n"); return; } void pf2(void) { printf("#########\\n"); return; } 该程序定义两个函数,分别打印不同的内容,该程序将被编译成.a静态库 编写调用程序call.c如下: extern void pf1(void); extern void pf2(void); int main(void) { pf1(); pf2(); return 0; } 该程序对静态库进行调用,调用静态库中的两个函数pf1和pf2。 编写Makefile如下: CC=gcc CPPFLAGS=-c OBJS = thread.o SOURCE = thread.c CALL_SOURCE=call.c LIB = libthread.a EXEC=call AR=ar thread: ${OBJS} ${CC} -c ${SOURCE} -o ${OBJS} ${AR} rcsv $(LIB) thread.o ${CC} -o ${EXEC} ${CALL_SOURCE} -L. -lthread .PHONY : clean clean : -rm -f ${OBJS} ${EXEC} ${LIB} Makefile文件实现对静态库程序编译成.a静态库,并且编译调用静态库的程序call.c为可执行文件call (4)程序执行效果如下: 题目二 聊天程序设计 1.设计内容及要求 1.1 聊天程序的基本要求 1、在QT(或miniGUI)编写聊天程序,交叉编译后下载到目标机,可实现两台目标机可发送文本聊天。 2、QT(或miniGUI)界面设计至少包括“发送”,“重置”两个按钮,要求必须把目标机的硬件键盘映射到QT设计的软件界面中,实现软件按钮的功能。 1.2 需实现的主要功能 (1) 客户端输入服务器IP和端口号。 (2) 服务器必须成功启动。 (3) 每一个客户端可以寻找服务器,并且与服务器建立连接。 (4) 客户端和服务器可以实现通信。 (5) 通信的内容可以显示在面板上。 (6) 利用QT设计界面,并下载到MagicARM2410上面。 2.需求分析 2.1 QT设计 QT设计编写和调试阶段使用周立功MagicARM2410箱子提供的QT for PC编译器,运行测试阶段使用QT for ARM编译器。 使用QT for PC阶段编译程序的步骤如下(以编译hello.cpp程序为例): (1)进入/x86-qtopia目录,运行set-env脚本,设置环境变量。 $ . set-env (2)进入hello目录,然后用progen工具生成工程文件hello.pro。 $ cd hello $ vi hello.cpp $ progen –t app.t –o hello.pro (3)使用tmake工具,生成hello工程的Makefile文件。 $ tmake -o Makefile hello.pro (4)修改Makefile文件,在LIBS变量中增加需要用到的库,然后输入make命令编译。 LIBS = $(SUBLIBS) -L$(QTDIR)/lib -lqte -lm -lstdc++ $ make (5)启动虚拟控制台,运行hello程序(主机须启动帧缓冲,必须能够访问/dev/fb0)。 $ cd /zylinux/x86-qtopia $ . set-env $ cd hello $ ./hello –qws 如果要将Hello程序发布到MagicARM2410上运行,还需进行以下工作: (6)进入/zylinux/arm-qtopia目录,并将hello工程复制到当前目录下。 $ cd /zylinux/arm-qtopia $ cp –av /zylinux/x86-qtopia/hello (7)运行当前目录下的set-env文件,重新设置环境变量,进入hello目录,使用tmake工具,重新生成Makefile文件。 $ . set-env $ cd hello $ tmake -o Makefile hello.pro (8)按照步骤(4)的方法修改包含库,编译,得到可执行文件hello,将hello文件添加到文件系统中,更新文件系统。 (9)插入USB鼠标和USB键盘,启动MagicARM2410。启动Qtopia的终端,运行hello程序。 利用同样的编译方法,客户端QT程序进行编译和调试。 2.1.1 客户端QT界面设计 客户端QT需要以下基本组件: (1)两个QEditLine,一个用于输入服务器IP地址(可读可写),另一个用于输入服务器端口号(可读可写)。 (2)三个按钮,一个用于连接服务器,另一个用于发送消息,最后一个用于清空发送区域数据。 (3)四个标签,用于显示不用组件的内容。 (4)两个MultiLineedit,一个用于显示通信内容(只读),一个作为发送区域(可读可写)。 2.2服务器端和客户端的数据传输方式 服务器端和客户端的数据是用TCP套接字来传输的。IP地址表示Internet上的计算机,端口号标识正在计算机运行的进程。端口号与IP地址的组合得出一个网络套接字。客户端使用linux C 函数socket建立到服务器的套接字连接。 当套接字连接socket建立后,可以利用linux C语言中的send函数和recv函数来实现简单的发送和接收消息。 2.3 服务器端功能设计 由于时间有限,服务器并没有用QT来编写界面,只是实现一个简单的控制程序。服务器主要实现有: (1)创建套接字 (2)对套接字、IP地址和端口号进行绑定 (3)进行监听 (4)等待客户端的连接 (5)对客户端接收和发送数据 2.4客户端功能设计 客户端使用QT设计界面,客户端实现的功能如下: (1)创建套接字 (2)连接服务器 (3)与服务器通信 3.总体设计 3.1 总体功能 根据对需求所做的分析,聊天程序需要实现的基本功能应包括以下几个方面: (1) 客户端输入服务器IP和端口号。 (2) 服务器必须成功启动。 (3) 每一个客户端可以寻找服务器,并且与服务器建立连接。 (4) 客户端和服务器可以实现通信。 (5) 通信的内容可以方便查看。 使用QT设计的界面简洁,大方,操作简单,方便,容易上手,用户可以快速掌握操作流程。 4. 详细设计 4.1 功能分析和描述 聊天工具包括两大功能模块:客户端和服务器端。具体实现的功能创建套接字、连接通信、接收数据显示和发送数据。 4.2 客户端功能模块 4.2.1 连接服务器 连接服务器之前需要解析服务器地址、创建套接字、设置sockaddr_in 结构体中相关参数。 4.2.2发送消息功能 连接功能是开始聊天前必须进行的步骤,连接成功之后,就可以进行发送消息了。 发送消息功能流程图如图2-5所示: 图2-5 发送消息功能流程图 4.2.3接收消息模块 连接成功后,就可以接收服务器发送过来的数据。 4.3 服务器功能模块 服务器的设计并没有使用QT编写界面,服务器端实现的功能有: 图2-8 服务器模块流程图 5. 代码实现 5.1 客户端代码 5.1.1 QT构造和析构函数 EditDemo::EditDemo( QWidget *parent, const char *name):QWidget(parent, name) { Server Port:", this); /*add the connect button*/ /*add the button*/ } EditDemo::~EditDemo() { if(socket_flag == 1)/*if create socket success*/ { close(sockfd); /*Close the socket*/ printf("Close the socket\\n"); } } 5.1.2 创建套接字 void EditDemo::CreateSocket(void) { 地址解析函数*/ ; 创建socket*/ ; 设置sockaddr_in 结构体中相关参数*/ } 5.1.3 创建线程 void *task(void *arg) { int recvbytes = 0; while(1) { if((recvbytes = recv(sockfd, buf, BUFFER_SIZE, 0)) > 0) { } } } void CreateThread(void) { } 5.2 服务器端代码 #define PORT 4321 #define BUFFER_SIZE 1024 #define MAX_QUE_CONN_NM 5 int main() { 建立socket连接*/ ; 设置sockaddr_in 结构体中相关参数*/ 使得重复使用本地地址与套接字进行绑定 */ 绑定函数bind*/ ; 调用listen函数*/ ; 调用accept函数,等待客户端的连接*/ ; 调用recv函数接收客户端的请求*/ ; printf("Send a message: %s\\n", buf); ; } 6.功能实现 7.总结 通过这次嵌入式linux程序设计和编程,对linux的编程有了更深的了解。特别是对QT编程有了很大的进步,通过测试,编写的程序都能顺利运行。由于时间紧迫,对服务器部分没有用QT来设计界面,只是编写客户端的QT程序,并成功仿真运行。 在编写代码的过程中,我非常认真,努力解决BUG。 8.致谢 感谢学院给我们这次嵌入式linux课程设计的机会,同时也感谢帮组我的老师和同学。 9.参考文献 [1] 嵌入式Linux应用程序开发标准教程.人民邮电出版社。华清远见嵌入式培训中心。2010.7 [2] ZLG ARMMAGIC2410实验指导、资料。
