课程设计成果 院(系):_电气与信息工程学院_ 班 级: 计科普 学生姓名: 学 号: 设计地点(单位)___ _________ _______ 设计题目:_____ 聊天程序设计_________________________ _ 完成日期:
2011 年 9 月 X 日 指导教师评语: _______________________________________ _________________________________________________________________________________________________________________________________________________________________________________________________________ 成绩(五级记分制):______ __________ 教师签名:_________________________ 摘要 嵌入式linux在电子行业的应用很广泛,学习嵌入式linux显得非常重要。这次课程设计的主要目的是检验上学期学习linux后的效果。通过基础题的代码编写,熟悉linux C语言编程技巧。通过完成聊天程序的设计,熟悉linux底层编程。利用QT设计界面,缩短开发时间。
关键字:嵌入式linux;
基础题;
聊天程序;
QT 目 录 课程设计任务书 - 4 - 题目一 基础题 - 6 - 1 设计内容及要求 - 6 - 1.1 二小题 - 6 - 1.2三小题 - 6 - 1.3四小题 - 7 - 1.3五小题 - 9 - 1.4六小题 - 11 - 1.5七小题 - 11 - 1.6八小题 - 13 - 题目二 聊天程序设计 - 15 - 1.设计内容及要求 - 15 - 1.1 聊天程序的基本要求 - 15 - 1.2 需实现的主要功能 - 15 - 2.需求分析 - 15 - 2.1 QT设计 - 15 - 2.2服务器端和客户端的数据传输方式 - 16 - 2.3 服务器端功能设计 - 16 - 2.4客户端功能设计 - 17 - 3.总体设计 - 17 - 3.1 总体功能 - 17 - 4. 详细设计 - 17 - 4.1 功能分析和描述 - 17 - 4.2 客户端功能模块 - 17 - 4.3 服务器功能模块 - 19 - 5. 代码实现 - 20 - 5.1 客户端代码 - 20 - 5.2 服务器端代码 - 22 - 6.功能实现 - 23 - 7.总结 - 23 - 8.致谢 - 23 - 9.参考文献 - 23 - 课程设计任务书 设计题目:基础题目 学生姓名 课程名称 嵌入式linux课程设计 专业班级 计科普 地 点 起止时间 2011-8-29至2011-9-9 设计内容及要求 GCC编译器的使用,LINUX系统C程序设计编译、调试方法 1、 编写程序将数组内容倒置a[]=”123456789”。
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课程设计 专业班级 计科普 地 点 起止时间 2011-8-29至2011-9-9 设计内容及要求 1、在QT(或miniGUI)编写聊天程序,交叉编译后下载到目标机,可实现两台目标机可发送文本聊天 2、QT(或miniGUI)界面设计至少包括“发送”,“重置”两个按钮,要求必须把目标机的硬件键盘映射到QT设计的软件界面中,实现软件按钮的功能。
选作:发送文件。
设计 参数 进度 要求 做完基础题目之后开始做本题目。
参考资料 1、嵌入式Linux应用程序开发标准教程.人民邮电出版社。华清远见嵌入式培训中心。2010.7 2、ZLG ARMMAGIC2410实验指导、资料。
其它 说明 1.本表应在每次实施前一周由负责教师填写二份,学院审批后交学院教务办备案,一份由负责教师留用。2.若填写内容较多可另纸附后。3.一题多名学生共用的,在设计内容、参数、要求等方面应有所区别。
系主任:
指导教师:
2011年 9 月 5 日 题目一 基础题 1 设计内容及要求 1.1 二小题 (1) 编写程序将数组内容倒置a[]=”123456789”。
(2) 程序代码实现如下:
char a[]=“0123456789“; ① 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<strlen(a);i++) ③ printf(“%c“,a[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<strlen(A);i++) ④ B[i] = *tmp++; for(i = 0;i<strlen(A);i++) ⑤ printf(“%c“,B[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(“\tThread %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的时间,意义在于模拟每个任务的到达时间是随机的,更加形象看出线程之间的并行执行。
① 创建线程执行函数,程序中2个线程重用一个执行函数。
② 让线程延时,延时时间随机产生。
③ 线程退出。
④ 创建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“,“send the msg“); 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) 代码分析如下:
该程序实现了使用消息队列进行线程之间的通信,包括消息队列的创建、消息发送和读取、消息队列的撤销和删除等操作。
① 定义消息队列消息结构体 ② 创建线程0,用于消息队列发送消息 ③ 添加消息到消息队列 ④ 退出线程 ⑤ 创建线程1,用于消息队列读取消息 ⑥ 读取消息队列 ⑦ 根据不同的路径和关键表示产生标准的key ⑧ 创建消息队列 ⑨ 等待线程 ⑩ 从系统内核中移走消息队列 (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之后。
① 创建线程0执行函数,用于打印字符’A’ ② 打印字符’A’ ③ 对信号量1进行V操作 ④ 延时2s ⑤ 退出线程 ⑥ 创建线程1执行函数,用于打印字符’B’ ⑦ 对信号量1进行P操作 ⑧ 打印字符’B’ ⑨ 延时1s ⑩ 创建线程 (4)程序执行效果如下:
1.6八小题 (1)通过Makefile将project中的一个.c编译成.a,另一个.c调用.a的函数,要求实现静态库的生成和调用,运行结果正确。
(2)编写静态库程序thread.c如下:
#include <stdio.h> 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) { QGridLayout *grid = new QGridLayout(this, 6, 3, 0, 0); //IP addr QLabel *label1 = new QLabel( “IP Addr:“, this); grid->addWidget( label1, 0, 0 ); edit_ip = new QLineEdit(this); edit_ip->setText(“127.0.0.1“); grid->addWidget( edit_ip, 0, 1 ); //Server Port QLabel *label2 = new QLabel( “Server Port:“, this); grid->addWidget( label2, 1, 0); edit_port = new QLineEdit(this); edit_port->setText(“4321“); grid->addWidget( edit_port, 1, 1); QPushButton *btn = new QPushButton(“Connect“, this); /*add the connect button*/ grid->addWidget( btn, 2, 1); /*add the button*/ connect(btn, SIGNAL(clicked()), this, SLOT(ButtonConnect())); //multiline input mode QLabel *label3 = new QLabel( “Contract Area:“, this); grid->addWidget(label3, 3, 0); multiedit_contract = new QMultiLineEdit(this); multiedit_contract->setReadOnly(1);//set read only grid->addWidget(multiedit_contract,3,1); QLabel *label4 = new QLabel( “Send Area:“, this); grid->addWidget(label4, 4, 0); multiedit_send = new QMultiLineEdit(this); grid->addWidget(multiedit_send,4,1); QPushButton *button_send = new QPushButton(“Send“, this); /*add the send button*/ grid->addWidget( button_send, 5, 0); connect(button_send, SIGNAL(clicked()), this, SLOT(ButtonSend())); QPushButton *button_clear = new QPushButton(“Clear“, this); /*add the clear button*/ grid->addWidget( button_clear, 5, 1); connect(button_clear, SIGNAL(clicked()), this, SLOT(ButtonClear())); } 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) { /*地址解析函数*/ host = gethostbyname(edit_ip->text());
/*创建socket*/ sockfd = socket(AF_INET,SOCK_STREAM,0); /*设置sockaddr_in 结构体中相关参数*/ serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(atoi(edit_port->text())); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero), 8); } 5.1.3 创建线程 void *task(void *arg) { int recvbytes = 0; char buf[BUFFER_SIZE]= {0}; arg = arg; while(1) { if((recvbytes = recv(sockfd, buf, BUFFER_SIZE, 0)) > 0) { multiedit_contract->append(“Server said: “+(QString)buf); } } } void CreateThread(void) { int result = 0; result = pthread_create(&thread1, NULL, task, (void *)0); /*Create Thread*/ } 5.2 服务器端代码 #define PORT 4321 #define BUFFER_SIZE 1024 #define MAX_QUE_CONN_NM 5 int main() { struct sockaddr_in server_sockaddr, client_sockaddr; int sin_size, recvbytes; unsigned int sendbytes = 0; int sockfd, client_fd; char buf[BUFFER_SIZE]; /*建立socket连接*/ sockfd = socket(AF_INET,SOCK_STREAM,0);
/*设置sockaddr_in 结构体中相关参数*/ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero), 8); int i = 1;/* 使得重复使用本地地址与套接字进行绑定 */ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); /*绑定函数bind*/ ibind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr); /*调用listen函数*/ listen(sockfd, MAX_QUE_CONN_NM); /*调用accept函数,等待客户端的连接*/ client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size); /*调用recv函数接收客户端的请求*/ while(1) { memset(buf , 0, sizeof(buf)); recvbytes = recv(client_fd, buf, BUFFER_SIZE, 0); if(recvbytes > 0) { printf(“Received a message: %s\n“, buf); printf(“Send a message: %s\n“, buf); (sendbytes = send(client_fd, buf, strlen(buf), 0); } } close(sockfd); exit(0); } 6.功能实现 7.总结 通过这次嵌入式linux程序设计和编程,对linux的编程有了更深的了解。特别是对QT编程有了很大的进步,通过测试,编写的程序都能顺利运行。由于时间紧迫,对服务器部分没有用QT来设计界面,只是编写客户端的QT程序,并成功仿真运行。
在编写代码的过程中,我非常认真,努力解决BUG。
8.致谢 感谢学院给我们这次嵌入式linux课程设计的机会,同时也感谢帮组我的老师和同学。
9.参考文献 [1] 嵌入式Linux应用程序开发标准教程.人民邮电出版社。华清远见嵌入式培训中心。2010.7 [2] ZLG ARMMAGIC2410实验指导、资料。
扩展阅读文章
推荐阅读文章
诚达文秘网 https://www.rk1k.cn Copyright © 2019-2025 . 诚达文秘网 版权所有
Powered by 诚达文秘网 © All Rights Reserved. 备案号:浙ICP备19042126号-1