重庆科技学院
课程设计成果
院(系):_电气与信息工程学院_ 班 级: 计科普0802
学生姓名: 学 号:
设计地点(单位)___ _I315 __________ _______
设计题目:_____ 聊天程序设计_________________________ _
完成日期: 2011 年 9 月 5 日
指导教师评语: _______________________________________
_________________________________________________________________________________________________________________________________________________________________________________________________________
成绩(五级记分制):______ __________
教师签名:_________________________
摘要
嵌入式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 -
课程设计任务书
设计题目:基础题目
设计题目:聊天程序设计
系主任: 指导教师: 20##年 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实验指导、资料。