2024年4月28日发(作者:)

成都学院(成都大学)课程设计报告

linux TCP服务器/客户端通信程序

摘要:随着计算机网络的不断发展,网络编程变得越来越重要,除了简单的WEB编程外,还包括利

用套接字(Socket)进行客户/服务器应用程序的设计。本文先对与套接字相关的概念和函数作了一

般性介绍,并提出多线程的编程方法和设计流程,也就具体的工程实例进行了流程分析。本文中,

对计算机的网络模型进行了简要的分析,并对TCP的握手模型进行了概述;在多线程编程中,本文

详细分析了多线程的互斥模型,讲解了多种线程之间的同步方法,并在程序设计中得到体现,详细

讲述了Linux中的TCP服务器/客户端通信程序,并对结果进行了验证。

关键字:网络编程 ;多线程;套接字

I

成都学院(成都大学)课程设计报告

目 录

绪论 .............................................................................1

1. 课程背景 ..................................................................... 1

2. 选题的目的和意义 ............................................................. 1

3. 国内外研究现状 ............................................................... 1

4. 主要研究内容 ................................................................. 1

第1章 需

求分析 ..................................................................3

1.1 设计目的 .................................................................... 3

1.2 课题要求 .................................................................... 3

1.3 任务分析 .................................................................... 3

第2章 环境

搭建 ..................................................................4

2.1 Ubuntu系统安装 .............................................................. 4

2.2 开发环境搭建 ................................................................ 7

2.2.1 NFS环境介绍 ........................................................... 7

2.2.2 NFS安装 ............................................................... 7

2.2.3 挂载NFS文件系统 ...................................................... 7

2.2.4 交叉工具安装 .......................................................... 8

第3章 软

件设计 ..................................................................9

3.1 TCP/IP协议 .................................................................. 9

3.1.1 网络模型 .............................................................. 9

3.1.2 TCP连接 ............................................................... 9

3.2 多线程编程 ................................................................. 10

3.3 Socket网络编程模型 ......................................................... 12

3.3.1 TCP Server编程模型 ................................................... 12

3.3.2 TCP Client编程模型 ................................................... 13

3.4 程序设计 ................................................................... 13

3.4.1 主要内容 ............................................................. 13

II

成都学院(成都大学)课程设计报告

3.4.2 服务器端程序设计 ..................................................... 14

3.4.3 客户端程序设计 ....................................................... 16

第4章 综合测试 .................................................................... 18

4.1 功能测试 ................................................................... 18

第5章

结论 .....................................................................20

参考文献 ........................................................................21

附录一 服务器端程序 .............................................................22

附录二 客户端程序 ...............................................................32

III

成都学院(成都大学)课程设计

绪论

Linux经历了20多年的发展,已经成为了一个功能强大而稳定的操作系统,在嵌入式系统中也

得到广泛的运用,伴随着物联网技术的普及,网络通信在嵌入式系统中扮演着举足轻重的作用。

1. 课程背景

随着时代的发展,网络通信在我们的生活中愈来愈重要,在互联网技术基础上延伸和扩展来的

物联网技术,正逐渐改变着我们的世界。互联网在现实生活中的应用很广泛,互联网给我们的现实

生活带来了很大的方便;互联网是全球性的,这也就意味着我们能够打破时空的界限,通过互联网

接触到世界的每一个角落;因为互联网的强大力量,这个时代的文明发展得到极大地提高。

2. 选题的目的和意义

由于互联网超乎寻常的强大力量,改变了这个时代的交流方式,改变着人们的生活,未来,我

们还将在互联网领域得到更多的进步,会影响生活中的方方面面。

3. 国内外研究现状

互联网从诞生至今,让人类文明得到巨大的推动,伴随着互联网的发展,各种依托互联网的技

术得到迅速发展,Linux操作系统依据其优良的性能和网络功能,在各个领域都得到极大的普及。

21世纪,是互联网发展的有一个阶段,我们国家已经将互联网的发展提升到了战略高度,明确表示

要建成互联网强国,我国到目前为止,已经诞生了一大批优秀的互联网企业,全世界都将在互联网

的推动下,进入一个全新的时代。

4. 主要研究内容

设计TCP服务器程序,使用多线程实现”生产者-消费者“模型,建立TCP服务器,响应客户端

请求,发送客户端指定的请求数据。

主要包括以下内容:

(1) 创建线程持续产生数据,数据包含(学号,姓名(拼音),年龄,身高,体重,当前系统

时间(纳秒数)使用gettimeofday(),可使用队列/多维数组存储数据;

(2) 创建TCP服务器线程,响应多个客户端的连接,读取队列/数组,向客户端发送指定“学号”

的数据。设计TCP服务器程序;

1

成都学院(成都大学)课程设计

(3) 创建TCP客户端接收线程,连接服务器并请求指定“学号”的数据,接收数据并存储在文

件中。要求存储有意义的数据,由于TCP是基于字节流的特征,需要做组包处理;

2

成都学院(成都大学)课程设计

第1章 需求分析

1.1 设计目的

通过对专业知识的熟练运用,理解Linux网络编程的流程,了解互联网的基本架构,熟悉多线

程编程的思想。同时,通过本课程设计,可以培养以下能力:

(1) 独立工作能力与创造力;

(2) 综合运用专业及基础知识的能力;

(3) 解决实际工程技术问题的能力;

(4) 查阅图书资料、产品手册和各种工具书的能力;

(5) 书写技术报告和编制技术资料的能力。

1.2 课题要求

使用多线程实现”生产者-消费者“模型,建立TCP服务器,响应客户端请求,发送客户端指

定的请求数据。

1.3 任务分析

创建线程持续产生数据,数据包含(学号,姓名(拼音),年龄,身高,体重,当前系统时间

(纳秒数)使用gettimeofday),可使用队列/多维数组存储数据。理解常用的数据结构,熟练掌

握C编程语言。

创建TCP服务器线程,响应多个客户端的连接,读取队列/数组,向客户端发送指定“学号”

的数据,设计TCP服务器程序,掌握网络编程中服务器端的编程流程。

创建TCP客户端接收线程,连接服务器并请求指定“学号”的数据,接收数据并存储在文件中。

要求存储有意义的数据,由于TCP是基于字节流的特征,需要做组包处理。掌握网络编程中客户端

的编程流程。

最终的目的是熟练掌握网络编程的编程方法,理解常用的数据结构的基本思想,掌握编程语言,

理解多线程编程在实际工程中的应用。

3

成都学院(成都大学)课程设计

第2章 环境搭建

2.1 Ubuntu系统安装

考虑到Windows系统的普及程度,本课程实际将利用虚拟机来进行开发,首先我们需要搭建虚

拟机开发环境。

1. 创建虚拟机

图2.1.1

2. 选择操作系统

图2.1.2

4

成都学院(成都大学)课程设计

3.配置处理器和内存

图2.1.3

图2.1.4

5

成都学院(成都大学)课程设计

4. 安装系统

图2.1.5

5. 安装成功界面

图2.1.6

6

成都学院(成都大学)课程设计

2.2 开发环境搭建

2.2.1 NFS环境介绍

NFS(Network File System)即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许

网络中的计算机之间通过TCP/IP网络共享资源。在NFS的应用中,本地NFS的客户端应用可以透明

地读写位于远端NFS服务器上的文件,就像访问本地文件一样。

2.2.2 NFS安装

是网络文件系统系统的缩写,可以用于Linux和Linux之间传递文件,实现数据共享。

安装命令如下:

apt-get install nfs-kernel-server

2.修改配置文件

打开/etc/exports文件,增加mount -t nfs/NFS (rw,sync,no_root_squash,no_subtree_check)

开发板和其他 Linux 主机可以通过网络访问/NFS 目录。

3.启动NFS

sudo service rpcbind start

sudo service nfs-kernel-server start

2.2.3 挂载NFS文件系统

mount -t nfs -o intr,nolock,rsize=1024,wsize=1024 192.168.1.86:/opt/ /mnt

7

成都学院(成都大学)课程设计

2.2.4 交叉工具安装

1.在/usr/local/下建立交叉编译器的安装目录arm:

sudo mkdir /usr/local/arm

2.将下载的交叉编译器包解压到/usr/local/arm目录下:

sudo tar jxvf 2 -C /usr/local/arm/

3.解压成功后,修改PATH环境变量:

sudo vim /etc/profile

在文件为加入交叉编译器arm-linux-所在的路径:

export PATH=$PATH:/usr/local/arm/4.2.2-eabi/usr/bin

4.更新一下配置文件/etc/profile:

source /etc/profile

8

成都学院(成都大学)课程设计

第3章 软件设计

3.1 TCP/IP协议

3.1.1 网络模型

telnet ftp 应用层

OSI参考模型

应用层

表示层

会话层

TCP/IP参考模型

TCP UDP

传输层

应用层

ICMP IGMP

网络层

IPv4、IPv6

传输层

网络层

数据链路层

传输层

网络层

ARP、RARP

网络接口层

网络接口层

物理层

MPLS

图 3-1-1 网络模型

如图3-1-1所示,在TCP/IP协议中,将互联网划分成为应用层、传输层、网络层、网络接口层,

其中网络接口层的主要功能是提供二进制传输和介质访问的功能;网络层负责IP寻址和路由,其中

要考虑路由算法,拥塞控制等问题;传输层负责应用程序之间的连接;

3.1.2 TCP连接

TCP IP一般通过internet串行线路协议SLIP或点对点协议PPP在串行线上进行数据传送。

TCP/IP协议的基本传输单位是数据包 (datagram)。TCP协议负责把数据分成若干个数据包/段,并

给每个数据包加上包头,IP协议在每个包头上再加上接收端主机地址,这样数据找到自己要去的地

方。如果传输过程中出现数据丢失、数据失真等情况,TCP协议会自动要求数据重新传输并重新组

包。TCP协议保证数据传输的质量,IP协议保证数据的传输。数据在传输时每通过一层就要在数据

上加个包头,其中数据供接收端同一层协议使用,而在接收端每经过一层要把用过的包头去掉,这

样来保证传输数据的格式完全一致。TCP/IP协议需要针对不同的网络进行不同的设置,且每个节点

9

成都学院(成都大学)课程设计

一般需要一个“IP地址”、一个“子网掩码”、一个“默认网关”。不过可以通过动态主机配置协

议(DHCP),给客户端自动分配一个IP地址,这样避免了出错也简化了TCP/IP协议的设置。

如图3-1-2所示,TCP是通过3次握手建立的:

1. 客户端给服务器发送SYN(syn = j)包,进入SYN_SEND状态。

2. 服务器接收到SYNC包,确认客户的SYN(ack = j+1),同时自己也发送一个SYN包(syn

= k),把它俩都发送出去,服务器进入SYN_SEND状态。

3. 客户端收到服务器的SYN+ACK包,向服务器发送ACK(ack = k+1),客户端和服务器都进

入ESTABLISHED状态。此时,连接已经建立完毕,可以相互发送发送消息。

图 3-1-2 三次握手示意图

3.2 多线程编程

进程是系统中程序执行和资源分配的基本单位。每个进程都拥有自己的数据段、代码段和堆栈

段,这就造成了进程在进行切换等操作时都需要有比较复杂的上下文切换等动作。为了进一步减少

处理机的空转时间,支持多处理器以及减少上下文切换开销,进程在演化中出现了另一个概念——

线程。它是进程内独立的一条运行路线,处理器调度的最小单元,也可以称为轻量级进程。线程可

以对进程的内存空间和资源进行访问,并与同一进程中的其他线程共享。因此,线程的上下文切换

的开销比创建进程小很多。

同进程一样,线程也将相关的执行状态和存储变量放在线程控制表内。一个进程可以有多个线

程,也就是有多个线程控制表及堆栈寄存器,但却共享一个用户地址空间。要注意的是,由于线程

共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响。由此

可知,多线程中的同步是非常重要的问题,以下是线程同步用到的一些方法:

10

成都学院(成都大学)课程设计

1. 互斥锁

互斥锁是用一种简单的加锁方法来控制对共享资源的原子操作。这个互斥锁只有两种状态,也

就是上锁和解锁,可以把互斥锁看作某种意义上的全局变量。在同一时刻只能有一个线程掌握某个

互斥锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经被上锁的互

斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。可以说,这把互斥锁保证让每个线

程对共享资源按顺序进行原子操作。

互斥锁可以分为快速互斥锁、递归互斥锁和检错互斥锁。这三种锁的区别主要在于其他未占有

互斥锁的线程在希望得到互斥锁时是否需要阻塞等待。快速锁是指调用线程会阻塞直至拥有互斥锁

的线程解锁为止。递归互斥锁能够成功地返回,并且增加调用线程在互斥上加锁的次数,而检错互

斥锁则为快速互斥锁的非阻塞版本,它会立即返回并返回一个错误信息。

互斥锁机制主要包括下面的基本函数:

1)互斥锁初始化:pthread_mutex_init()

2)互斥锁上锁:pthread_mutex_lock()

3)互斥锁判断上锁:pthread_mutex_trylock()

4)互斥锁解锁:pthread_mutex_unlock()

5)消除互斥锁:pthread_mutex_destroy()

2. 信号量

信号量也就是操作系统中所用到的PV原子操作,它广泛用于进程或线程间的同步与互斥。信号

量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

PV原子操作是对整数计数器信号量sem的操作。一次P操作使sem减一,而一次V操作使sem

加一。进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem的值大

于等于零时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem的值小于零时,该

进程(或线程)就将阻塞直到信号量sem的值大于等于0为止。

信号量机制主要包括下面的基本函数:

1)创建信号量: sem_init()

2)等待信号量:sem_wait()和sem_trywait()

3)唤醒进程:sem_post()

4)获取信号量: sem_getvalue()

5)删除信号量: sem_destroy()

11

成都学院(成都大学)课程设计

3.3 Socket网络编程模型

在Linux中的网络编程是通过socket接口来进行的。socket是一种特殊的I/O接口,它也是

一种文件描述符。它是一种常用的进程之间通信机制,通过它不仅能实现本地机器上的进程之间的

通信,而且通过网络能够在不同机器上的进程之间进行通信。

源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请

求的服务器和服务,它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连

接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议

端口。

3.3.1 TCP Server编程模型

进行版本协商

图 3-2-1 Server编程模型

接收或者发送数据

设置为监听状态

创建套接字

接受客户端的连接请求

关闭套接字

1)进行版本协商(WSAStartup)

2)创建一个套接字(socket())

3)将套接字设为监听状态(listen())

4)接受客户端的连接请求(accept())

5)发送或者接收数据(send()/recv())

6)关闭套接字(close())

12

成都学院(成都大学)课程设计

3.3.2 TCP Client编程模型

连接到服务器

创建套接字

进行版本协商

关闭套接字

图 3-2-2 Client编程模型

接收或者发送数据

1)进行版本协商(WSAStartup)

2)创建一个套接字(socket())

3)连接到服务器(connect())

4)发送或者接收函数(send()/recv())

5)

关闭套接字(close())

3.4 程序设计

3.4.1 主要内容

设计TCP服务器程序,使用多线程实现”生产者-消费者“模型,建立TCP服务器,响应客户端

请求,发送客户端指定的请求数据。

1.创建线程持续产生数据,数据包含(学号,姓名(拼音),年龄,身高,体重,当前系统时间(纳

秒数)使用gettimeofday),可使用队列/多维数组存储数据。

2.创建TCP服务器线程,响应多个客户端的连接,读取队列/数组,向客户端发送指定“学号”的数

据。设计TCP服务器程序。

3.创建TCP客户端接收线程,连接服务器并请求指定“学号”的数据,接收数据并存储在文件中。

要求存储有意义的数据,由于TCP是基于字节流的特征,需要做组包处理

13

成都学院(成都大学)课程设计

3.4.2 服务器端程序设计

以下是服务器端程序的主函数部分,在主函数中,首先创建了一个新的线程,然后按照网络编

程模型中服务器端的编程方法进行了编程,详细程序设计请参考附件1.

void main(void)

{

/**************子线程相关*******************/

pthread_t reader = -1; //read进程的进程号

pthread_mutex_init(&mutex,NULL); //初始化 互斥锁

/*初始化数据*/

int i = 0;

for(i=0; i<5; i++)

{

}

information[i].name = NAME[i];

information[i].age = AGE[i];

information[i].number = NUM[i];

information[i].high = HIGH[i];

information[i].weigh = WIGHT[i];

/*创建线程,产生数据*/

pthread_create(&reader,NULL,(void*)&writer_function,NULL);

/************主线程相关**********************/

//创建socket

nListenSock =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

if(nListenSock<0)

{

}

//设置socket选项

14

printf("create listen socket errorn");

return;

成都学院(成都大学)课程设计

int nValue=1;

if(setsockopt(nListenSock,SOL_SOCKET,SO_REUSEADDR,(char*)&nValue,sizeof(int))<0)

{

}

//绑定

struct sockaddr_in localAddr;

memset(&localAddr,0x0,sizeof(localAddr));

_family =AF_INET;

_addr.s_addr =htonl(INADDR_ANY);

_port =htons(10000);

if(bind(nListenSock,(struct sockaddr*)&localAddr,sizeof(struct sockaddr))<0)

{

}

//监听

if(listen(nListenSock,5)<0)

{

}

//创建客户端的线程

int nSrvThreadId =1;

nThreadFlag =1;

printf("listen error!n");

close(nListenSock);

return;

printf("bind liste sock fail!n");

close(nListenSock);

return;

printf("set option SO_REUSEADDR fail!n");

close(nListenSock);

return;

15

成都学院(成都大学)课程设计

if(pthread_create((pthread_t*)&nSrvThreadId,NULL,(void*)&serverThreadProc,NULL)<0)

}

{

}

//等待退出

while(1)

{

}

if(pthread_join(nSrvThreadId,NULL)!=0)

{

}

return;

printf("create server thread fail!n");

close(nListenSock);

return;

3.4.3 客户端程序设计

以下是客户端的程序设计,代码片段太过冗长,详细的程序设计请参考附件2。

void main(int argc, char** argv)

{

//创建套接字

......

int sockfd=-1;

sockfd =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

if(sockfd<0)

{

}

printf("socket create errorn");

return;

16

成都学院(成都大学)课程设计

//建立连接

......

//创建交互进程

......

}

while(1) //建立链接

{

}

if(connect(sockfd,

{

}

sleep(1);

printf("connectedn");

break;

(struct sockaddr*)&serverAddr,

sizeof(serverAddr))==0)

pthread_t ipt_id;

if(pthread_create((pthread_t *)&ipt_id,NULL,(void*)&input,NULL)<0)

{

}

printf("create input thread fail!n");

close(ipt_id);

return;

17

成都学院(成都大学)课程设计

第4章 综合测试

4.1 功能测试

1.运行服务器端程序,处于监听状态,等待客户端来连接,当有客户端连接上,输出连接的客

户端的信息。

图 4-1-1 服务器运行

2.运行客户端程序,等待用户输入要从数据库中读取的信息编号,当用户输入要读取的信息标

号的时候,服务器响应客户端的请求,回复信息。

图 4-1-2 客户端运行

18

成都学院(成都大学)课程设计

3.在当前目录下生成一个.txt文件,内部包含客户端请求的信息。

图 4-1-3 生成文件

4.打开文件,内部为请求的信息。

图 4-1-4 文件内容

19

成都学院(成都大学)课程设计

第5章 结论

本次课程设计的内容是基于Linux操作系统的多线程网络编程,实现的功能是“生产者”,“消

费者”模型,建立TCP服务器,响应客户端请求,并发送客户端请求的数据。在程序的设计过程中

采用了多线程的编程方式,显著提高了程序运行的效率。

客户端与服务器通过TCP方式建立连接,使用的通讯函数接口为套接字,套接字在网络编程中

有着举足轻重的地位。通过本次课程设计,我们创建TCP服务器线程,响应多个客户端的连接,读

取队列/数组,向客户端发送指定“学号”的数据;创建TCP客户端接收线程,连接服务器并请求指

定“学号”的数据,接收数据并存储在文件中;在实现的这个过程中,利用数据结构中的队列构造

了数据表,方便程序访问,同时,也方便服务器端对数据的管理。

通过本次课程设计,掌握了Linux下的编程模式和编程方法,熟悉了Linux的基本操作;同时,

掌握了开发环境的搭建,常用的软件服务的安装,锻炼了实际的工程能力;通过多线程编程方法,

理解了线程和进程的区别和联系,掌握了创建线程和注销线程的方法;通过对套接字的使用,掌握

了在Linux下基于套接字的网络编程,理解了Linux下套接字编程在服务器端和客户端的编程流程,

了解了网络模型,提高了解决问题的能力。

20

成都学院(成都大学)课程设计

参考文献

[1] 范展源.深度实践嵌入式Linux系统移植.北京:机械工业出版社,2015.5

[2] 宋宝华.Linux设备驱动开发详解.北京:机械工业出版社,2015.7

[3] 谭浩强.C程序设计.北京:清华大学出版社,2010.6

[4] 陈文智.嵌入式系统设计与原理.北京:清华大学出版社,2011.5

[5] 宋敬彬.Liunx网络编程.北京:清华大学出版社,2010.6

21

成都学院(成都大学)课程设计

附录一 服务器端程序

/********************************************

文件名: server.c

文件描述: 嵌入式课程设计程序

完成日期:2017年9月8日

作者:陈凯

联系方式:****************

********************************************/

#include

#include

#include

#include

#include

#include

#include

#include

/*函数申明*/

void writer_function(void);

void serverThreadProc(void *);

//链表结点结构体

typedef struct _CLIENT_INFO_

{

char szClientIp[16];

int nClientPort;

int nClientSock;

struct _CLIENT_INFO_ *pNext;

struct _CLIENT_INFO_ *pPre;

}CLIENT_INFO;

/*数据类型*/

typedef struct datatype

{

unsigned char number;

unsigned char age;

unsigned char high;

unsigned char weigh;

22

成都学院(成都大学)课程设计

long time;

char *name;

}Datatype;

/****协议包结构****/

typedef struct pro_package

{

char head; //开始标志为 设定为0x7E 1

int lenth; //包的数据部分长度 4

unsigned char flag; //0 : cmd 1:data 1

unsigned char stop; //0 stop 1:send 1

unsigned char num; //标明发送信息 1

unsigned char data[200]; //数据部分

}Pro_package;

/*创建缓存区*/

typedef struct queue

{

Datatype buffer[5];

int b_tail;

int b_head;

}queue;

/*初始化队列*/

queue Queue =

{

.b_tail = 0,

.b_head = 0,

};

/*数据初始化*/

char *NAME[5] = {"wuhao", "chenkai","liumenglin","liujin","liufeng"};

unsigned char AGE[5] = {20,21,22,23,24};

unsigned char NUM[5] = {1,2,3,4,5};

unsigned char HIGH[5] = {175,174,173,175,165};

unsigned char WIGHT[5] = {58, 55, 62, 63, 50};

Datatype information[5];

/****全局变量***/

int nListenSock =-1; //server lisen socket

int nThreadFlag =0; // thread start/stop flag

CLIENT_INFO *pClientHead=NULL; //client list header

23

成都学院(成都大学)课程设计

struct timeval sys_time;

pthread_mutex_t mutex;

int buffer_has_item=0;

//主函数

void main(void)

{

/**************子线程相关*******************/

pthread_t reader = -1; //read进程的进程号

pthread_mutex_init(&mutex,NULL); //初始化 互斥锁

/*初始化数据*/

int i = 0;

for(i=0; i<5; i++)

{

information[i].name = NAME[i];

information[i].age = AGE[i];

information[i].number = NUM[i];

information[i].high = HIGH[i];

information[i].weigh = WIGHT[i];

}

/*创建线程,产生数据*/

pthread_create(&reader,NULL,(void*)&writer_function,NULL);

/************主线程相关**********************/

//创建socket

nListenSock =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

if(nListenSock<0)

{

printf("create listen socket errorn");

return;

}

//设置socket选项

int nValue=1;

if(setsockopt(nListenSock,SOL_SOCKET,

SO_REUSEADDR,(char*)&nValue,sizeof(int))<0)

{

printf("set option SO_REUSEADDR fail!n");

close(nListenSock);

return;

}

//绑定

struct sockaddr_in localAddr;

memset(&localAddr,0x0,sizeof(localAddr));

_family =AF_INET;

_addr.s_addr =htonl(INADDR_ANY);

_port =htons(10000);

24

成都学院(成都大学)课程设计

if(bind(nListenSock,(struct sockaddr*)&localAddr,sizeof(struct sockaddr))<0)

{

printf("bind liste sock fail!n");

close(nListenSock);

return;

}

//监听

if(listen(nListenSock,5)<0)

{

printf("listen error!n");

close(nListenSock);

return;

}

//创建客户端的线程

int nSrvThreadId =1;

nThreadFlag =1;

if(pthread_create((pthread_t*)&nSrvThreadId,NULL,(void*)&serverThreadProc,NULL)<0)

{

printf("create server thread fail!n");

close(nListenSock);

return;

}

//等待退出

while(1)

{

if(pthread_join(nSrvThreadId,NULL)!=0)

{

return;

}

}

}

/*处理客户端函数*/

void serverThreadProc(void *pPram)

{

int nMaxFd;

fd_set rset,allset;

struct timeval timeOut={0,20*1000};

FD_ZERO(&allset);

FD_SET(nListenSock,&allset);

nMaxFd =nListenSock;

CLIENT_INFO *pTcpClient;

25

成都学院(成都大学)课程设计

int nSelectR;

struct sockaddr_in clientAddr;

CLIENT_INFO *pTempClient;

unsigned char ucRcvBuf[100];

int nAddrSize =sizeof(clientAddr);

Pro_package package; //定义包结构

unsigned char send_buffer[200]; //发送缓存

int verify_flag = 0;

while(1) //while 循环

{

rset =allset;

nSelectR =select(nMaxFd+1,&rset,NULL,NULL,&timeOut);

if(nSelectR==-1) //若发生错误

{

continue;

}

else if(nSelectR==0) //超时

{

continue;

}

if(FD_ISSET(nListenSock,&rset)) // 检查nListenSock是否在套接字集合中

{

printf("accept client!n");

int nConnectSock =accept(nListenSock,(struct sockaddr*)&clientAddr,&nAddrSize);

if(nConnectSock==-1)

{

printf("nConnectSock -1n");

continue;

}

//保存客户端的信息

pTcpClient =(CLIENT_INFO*)malloc(sizeof(CLIENT_INFO));

pTcpClient->nClientSock =nConnectSock;

pTcpClient->nClientPort =ntohs(_port);

strcpy(pTcpClient->szClientIp,inet_ntoa(_addr));

26

成都学院(成都大学)课程设计

pTcpClient->pNext =NULL;

pTcpClient->pPre =NULL;

printf("client connected,ip:%s,port:%dn",

pTcpClient->szClientIp,

pTcpClient->nClientPort);

if(pClientHead==NULL)

{

pClientHead= pTcpClient;

}

else

{

pTempClient =pClientHead;

while(pTempClient->pNext!=NULL)

{

pTempClient =pTempClient->pNext;

}

pTempClient->pNext =pTcpClient;

pTcpClient->pPre =pTempClient;

}

//update fd set

FD_SET(nConnectSock,&allset);

if(nConnectSock>nMaxFd)

{

nMaxFd =nConnectSock;

}

}

else //对客户端的服务

{

//find the client

pTcpClient =pClientHead;

while(pTcpClient!=NULL)

{

int nRcvSock =pTcpClient->nClientSock;

if(FD_ISSET(nRcvSock,&rset)==0)

{

pTcpClient =pTcpClient->pNext;

continue;

}

//从客户端接收数据,需要修改

int nLen =recv(nRcvSock,ucRcvBuf,sizeof(ucRcvBuf),MSG_NOSIGNAL);

if(nLen<=0) //如果接收数据失败

{

//client disconnected

27

成都学院(成都大学)课程设计

if(nRcvSock!=-1)

{

shutdown(nRcvSock,SHUT_RDWR);

close(nRcvSock);

pTcpClient->nClientSock =-1;

}

//del this client info

FD_CLR(nRcvSock,&allset);

printf("client disconnected,ip:%s,port:%dn",

pTcpClient->szClientIp,

pTcpClient->nClientPort);

//del the client from list

if(pTcpClient->pPre!=NULL)

{

pTcpClient->pPre->pNext =pTcpClient->pNext;

}

else

{

pClientHead =pTcpClient->pNext;

}

if(pTcpClient->pNext!=NULL)

{

pTcpClient->pNext->pPre =pTcpClient->pPre;

}

//free

free(pTcpClient);

}

else //对客户端请求进行组包

{

int i;

for(i=0; i

{

if(ucRcvBuf[i] == 0x7E)

{

verify_flag = 1;

break;

}

}

if(verify_flag == 1)

{

28

成都学院(成都大学)课程设计

/**组包**/

= ucRcvBuf[i];

memcpy(&, &ucRcvBuf[i+1], 4);

= ucRcvBuf[i+5];

= ucRcvBuf[i+6];

= ucRcvBuf[i+7];

//不需要数据

i = 0; //i清0

int g;

if( == 0)

{

int k=0;

//查找数据,组包

for(k=0;k<5;k++)

{

if([k].number == ) //如果数字匹配

{

int sen_long = 8+8+strlen([k].name); //包总长度

//包头部分

send_buffer[0] = ;

memcpy(&send_buffer[1], &sen_long, 4); //头部为8个字节

send_buffer[5] = 1; //data

send_buffer[6] = ;

send_buffer[7] = ;

//数据部分

send_buffer[8] = [k].number;

send_buffer[9] = [k].age;

send_buffer[10] = [k].high;

send_buffer[11] = [k].weigh;

memcpy(&send_buffer[12], &[k].time, 4);

memcpy(&send_buffer[16],[k].name,strlen([k].name));

send(nRcvSock,send_buffer,sen_long,MSG_NOSIGNAL);

int g=0;

memset(&[k], 0, sizeof([k]));

}

}

}

else

printf("not cmd package!n");

29

成都学院(成都大学)课程设计

}

else

{

printf("false package!n");

}

verify_flag = 0;

}

pTcpClient =pTcpClient->pNext;

}

}

}

int nRetVal=1;

pthread_exit((void*)&nRetVal);

return;

}

/*生产函数*/

void writer_function(void)

{

int j=0;

int ret;

while(1)

{

ret = gettimeofday(&sys_time,NULL);//获取系统时间

if(ret < 0)

{

printf("get sys time fail!n");

continue;

}

pthread_mutex_lock(&mutex); //获取互斥锁 0

Queue.b_tail = (Queue.b_tail + 1)%5; //入队列

j = Queue.b_tail-1;

if(j == -1)

j = 4;

//printf("生产者: 生产一件产品!n");

[j].age= information[j].age; //往buffuer里面放入数据

[j].number = information[j].number;

[j].high = information[j].high;

[j].weigh = information[j].weigh;

[j].time = sys__sec;

//printf("%xn", [j].time);

30

成都学院(成都大学)课程设计

[j].name= information[j].name;

pthread_mutex_unlock(&mutex); //释放互斥锁

usleep(1000);

}

}

31

成都学院(成都大学)课程设计

附录二 客户端程序

/********************************************

文件名: client.c

文件描述: 嵌入式课程设计程序

完成日期:2017年9月8日

作者:陈凯

联系方式:****************

********************************************/

#include

#include

#include

#include

#include

#include

#include

/****协议包结构****/

typedef struct pro_package

{

char head; //开始标志为 设定为0x7E 1

int lenth; //包的数据部分长度 4

unsigned char flag; //0 : cmd 1:data 1

unsigned char stop; //0 stop 1:send 1

unsigned char num; //标明发送信息

unsigned char data[40]; //数据部分

}Pro_package;

/******接收数据结构体******/

typedef struct datatype

{

unsigned char number;

unsigned char age;

unsigned char high;

unsigned char weigh;

long time;

char *name;

}Datatype;

/*标准包*/

Pro_package test_package={

.head = 0x7E,

32

1

成都学院(成都大学)课程设计

.lenth = 8,

.flag = 0,

.stop = 1,

};

//global

int input_flag = 6;

void input();

char printf_buffer[300];

void main(int argc, char** argv)

{

if(argc!=3)

{

printf("%s: n",

argv[0]);

return;

}

//create socket

int sockfd=-1;

sockfd =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

if(sockfd<0)

{

printf("socket create errorn");

return;

}

//connect

struct sockaddr_in serverAddr;

memset(&serverAddr,0x0,sizeof(struct sockaddr_in));

_family =AF_INET;

_port =htons(atoi(argv[2]));

if(inet_pton(AF_INET,argv[1],

&_addr)<=0)

{

printf("input wrong server addrn");

close(sockfd);

return;

}

while(1) //建立链接

{

if(connect(sockfd,

(struct sockaddr*)&serverAddr,

sizeof(serverAddr))==0)

{

printf("connectedn");

33

成都学院(成都大学)课程设计

break;

}

sleep(1);

}

//创建交互线程

pthread_t ipt_id;

if(pthread_create((pthread_t *)&ipt_id,NULL,(void*)&input,NULL)<0)

{

printf("create input thread fail!n");

close(ipt_id);

return;

}

unsigned char send_buffer[8];

unsigned char ucRcvBuf[60];

int i,verify_flag;

Pro_package rcv_package;

Datatype rcv_data;

sleep(1);

while(1)

{

//打开文本文件

FILE* file_fd;

file_fd = fopen("./","w+");

if(file_fd == NULL)

{

printf("open file fail!n");

return;

}

int count = fprintf(file_fd, "%s", (char *)printf_buffer);

fclose(file_fd); //保存文件

sleep(1);//每次循环等待1秒

if(input_flag == 0) //停止

{

sleep(1);

printf("client stop recv!n");

break;

}

if(input_flag > 5) //非法输入

34

.

成都学院(成都大学)课程设计

{

}

printf("input number false, please reinput!n");

sleep(2);

continue;

int package_lenth = 8;

send_buffer[0] = test_;

memcpy(&send_buffer[1], &package_lenth, 4); //头部为8个字节

send_buffer[5] = test_; //控制信息

send_buffer[6] = test_;

send_buffer[7] = (char)input_flag;

send(sockfd, send_buffer, 8, MSG_NOSIGNAL);

/*打印发送的数据*/

/*int g;

printf("send data:n");

for(g=0; g<8; g++)

printf("%x ", send_buffer[g]);

*/

int nLen =recv(sockfd,ucRcvBuf,sizeof(ucRcvBuf),MSG_NOSIGNAL);

//printf("len :%dn", nLen);

if(nLen < 0) //接收失败

{

//disconnected

printf("disconnectedn");

close(sockfd);

return;

}

else if (nLen == 0) //如果正常链接.但没有数据发送

continue;

else //接收成功

{

/*1.找包头*/

//printf("2n");

for(i=0; i

{

if(ucRcvBuf[i] == 0x7E)

{

verify_flag = 1;

35

成都学院(成都大学)课程设计

break;

}

}

if(verify_flag == 1)

{

/**组包**/

rcv_ = ucRcvBuf[i];

memcpy(&rcv_, &ucRcvBuf[i+1], 4);

rcv_ = ucRcvBuf[i+5];

rcv_ = ucRcvBuf[i+6];

rcv_ = ucRcvBuf[i+7];

rcv_[0] = ucRcvBuf[i+8]; //age

rcv_[1] = ucRcvBuf[i+9]; //number

rcv_[2] = ucRcvBuf[i+10]; //high

rcv_[3] = ucRcvBuf[i+11]; //weigh

memcpy(&rcv_[4], &ucRcvBuf[i+12], 4);

//printf("time ls %d ")

//rcv_[8] ='0'; //加结束符

memcpy(&rcv_[8], &ucRcvBuf[i+16], rcv_-16);

rcv_[rcv_-8] ='0'; //加结束符

//printf("name is %s n", (char *)&rcv_[4]);

}

if(rcv_ == 1)

{

int offset = 0;

offset+=sprintf(printf_buffer+offset,"name:%sn",(char*)&rcv_[8]);

offset += sprintf(printf_buffer+offset," number: %d n", rcv_[0]);

offset += sprintf(printf_buffer+offset," age: %d n", rcv_[1]);

offset += sprintf(printf_buffer+offset,"high: %d n", rcv_[2]);

offset += sprintf(printf_buffer+offset,"weigh: %d n", rcv_[3]);

offset += sprintf(printf_buffer+offset," time: %ld n", /

(long)(rcv_[4] | rcv_[5]<<8 | /

rcv_[6]<<16 | rcv_[7]<<24));

printf("imformation:nr%s", printf_buffer);

}

else

36

成都学院(成都大学)课程设计

{

printf("It's not data package!n");

continue;

}

i = 0;

verify_flag = 0;

rcv_ = 0;

nLen = 0;

}

memset(ucRcvBuf, 0, strlen(ucRcvBuf));

memset(send_buffer, 0, strlen(send_buffer));

memset(&rcv_package, 0, sizeof(rcv_package));

memset(&rcv_data, 0, sizeof(rcv_data));

}

return;

}

/*等待用户按键输入线程*/

void input()

{

int cmd;

while(1)

{

scanf("%d", &input_flag);

}

}

37