《嵌入式linu网络编程》由会员分享,可在线阅读,更多相关《嵌入式linu网络编程(37页珍藏版)》请在金锄头文库上搜索。
1、第2章 TCP网络编程基础 TCP协议是TCP/IP协议中很重要的一个协议,它由于 传输的稳定性,在很多程序中都在使用,例如HTTP、FTP 等协议都是在TCP的基础上进行构建的。本章介绍TCP套接 字的编程基础知识,主要包含如下内容: 套接字编程的基础知识的部分,介绍套接字编程中经常 使用的套接字地址结构,对内核和应用层之间的内存数据传 递方式进行了简单的介绍。 TCP网络编程的流程部分,简单介绍TCP套接字服务器 、客户端的编程框架,对函数socket()、bind()、listen()、 accept()、connect()、close()函数进行了介绍,并提及如 何使用read()和w
2、rite()函数进行数据的读取和发送。 2.1 套接字编程基础知识 在进行套接字编程之前需要对基本的数据结构有所了解 。本节对套接字的地址结构定义的形式、如何使用套接字的 地址结构进行详细的介绍,并且对Linux操作系统中用户空 间和用户空间之间的交互过程进行简单的介绍,用户对网络 程序设计的方法有比较深入的了解。 2.1.1 套接字地址结构 进行套接字编程需要指定套接字的地址作为参数,不同 的协议族有不同的地址结构定义方式。这些地址结构通常以 sockaddr_开头,每一个协议族有一个唯一的后缀,例如对 于以太网,其结构名称为sockaddr_in。 1通用套接字数据结构 2实际使用的套接字
3、数据结构 3结构sockaddr和结构sockaddr_in的关系 2.1.1 套接字地址结构 2.1.2 用户层和内核层交互过程 套接字参数中有部分参数是需要用户传入的,这些参数 用来与Linux内核进行通信,例如指向地址结构的指针。通 常是采用内存复制的方法进行。例如bind()函数需要传入地 址结构struct sockaddr *my_addr和my_addr指向参数的 长度。 1向内核传入数据的交互过程 2内核传出数据的交互过程 2.1.2 用户层和内核层交互过程 2.2 TCP网络编程流程 TCP网络编程是目前比较通用的方式,例如HTTP协议 、FTP协议等很多广泛应用的协议均基于
4、TCP协议。TCP编 程主要为C/S模式,即服务器(S)、客户端(C)模式。 TCP网络编程的流程包含服务器和客户端两种模式,这两种 模式之间的程序设计的流程存在很大的差别。 2.2.1 TCP网络编程架构 TCP网络编程有两种模式,一种是服务器模式,另一种 是客户端模式。服务器模式创建一个服务程序,等待客户端 用户的连接,接收到用户的连接请求后,根据用户的请求进 行处理;客户端模式则根据目的服务器的地址和端口进行连 接,向服务器发送请求并对服务器的响应进行数据处理。 1服务器端的程序设计模式 2客户端的程序设计模式 3客户端与服务器的交互过程 2.2.1 TCP网络编程架构 2.2.2 创建
5、网络插口函数socket() 网络程序设计中的套接字系统调用函数socket()用来获得文 件描述符。 1函数socket()介绍 2应用层函数socket()和内核函数之间的关系 名称含义 PF_UNIX, PF_LOCAL本地通信 PF_INETIPv4 Internet协议 PF_INET6IPv6 Internet协议 PF_IPXIPX - Novell 协议 PF_NETLINK内核用户界面设备 PF_X25ITU-T X.25 / ISO-8208 协议 PF_AX25Amateur radio AX.25 协议 PF_ATMPVC原始ATM PVC访问 PF_APPLETALK
6、Appletalk PF_PACKET底层包访问 2.2.2 创建网络插口函数socket() 2.2.3 绑定一个地址端口对bind() 在建立套接字文件描述符成功后,需要对套接字进行地址和端口 的绑定,才能进行数据的接收和发送操作。 1函数bind()介绍 2函数bind()的例子 3应用层bind()函数和内核函数之间的关系 值含义备注 EADDRINUSE给定地址已经使用 EBADFsockfd不合法 EINVALsockfd已经绑定到其他地址 ENOTSOCKsockfd是一个文件描述符,不是socket描述符 EACCES地址被保护,用户的权限不足 EADDRNOTAVAIL接口不
7、存在或者绑定地址不是本地UNIX协议族,AF_UNIX EFAULTmy_addr指针超出用户空间UNIX协议族,AF_UNIX EINVAL地址长度错误,或者socket不是AF_UNIX族UNIX协议族,AF_UNIX ELOOP 解析my_addr是符号链接过多UNIX协议族,AF_UNIX ENAMETOOLONGmy_addr过长UNIX协议族,AF_UNIX ENOENT文件不存在UNIX协议族,AF_UNIX ENOMEM内存内核不足UNIX协议族,AF_UNIX ENOTDIR不是目录UNIX协议族,AF_UNIX EROFS socket节点应该在只读文件系统上UNIX协议族
8、,AF_UNIX 2.2.3 绑定一个地址端口对bind() 2.2.4 监听本地端口listen 在7.2.1小节中简单介绍了服务器模式的方式,服务器 模式中有listen()和accept()两个函数,而客户端则不需要这 两个函数。函数listen()用来初始化服务器可连接队列,服 务器处理客户端连接请求的时候是顺序处理的,同一时间仅 能处理一个客户端连接。当多个客户端的连接请求同时到来 的时候,服务器并不是同时处理,而是讲不能进行处理的客 户端连接请求放到等待队列中,这个队列的长度由listen() 函数来定义。 1函数listen()介绍 2函数listen()的例子 3应用层list
9、en()函数和内核函数之间的关系 2.2.4 监听本地端口listen 值含义 EADDRINUSE另一个socket已经在同一端口侦听 EBADF 参数sockfd不是合法的描述符 ENOTSOCK参数sockfd不是代表socket的文件描述符 EOPNOTSUPPsocket不支持listen操作 2.2.4 监听本地端口listen 2.2.5 接受一个网络请求accept() 当一个客户端的连接请求到达服务器主机侦听的端口时 ,此时客户端的连接会在队列中等待,直到使用服务器处理 接收请求。 函数accept()成功执行后,会返回一个新的套接口文件 描述符来表示客户端的连接,客户端连接
10、的信息可以通过这 个新描述符来获得。因此当服务器成功处理客户端的请求连 接后,会有两个文件描述符,老的文件描述符表示正在监听 的socket,新产生的文件描述符表示客户端的连接,函数 send()和recv()通过新的文件描述符进行数据收发。 1函数accept()介绍 2函数accept()的例子 3应用层accept()函数和内核函数之间的关系 2.2.6 连接目标网络服务器connect() 客户端在建立套接字之后,不需要进行地址绑定,就可 以直接连接服务器。连接服务器的函数为connect(),此函 数连接指定参数的服务器,例如IP地址,端口等。 1函数connet()介绍 2函数co
11、nnect的例子 3应用层connect()函数和内核函数之间的关系 2.2.6 连接目标网络服务器connect() 值含义 EACCES在AF_UNIX族协议中,使用路径名作为标识。EACCES表示目录 不可写或者不可访问。 EACCES/EPERM用户没有设置广播标志而连接广播地址或者连接请求被防火墙限 制。 EADDRINUSE本地地址已经在使用 EAFNOSUPPORT参数serv_addr的域sa_family不正确 EAGAIN本地端口不足 EALREADYsocket是非阻塞类型并且前面的连接没有返回 EBADF文件描述符不是合法的值 ECONNREFUSED连接的主机地址没有
12、侦听 EFAULTsocket结构地址超出用户空间 EINPROGRESSsocket是非阻塞模式,而连接不能立刻返回 EINTR 函数被信号中断 EISCONNsocket已经连接 ENETUNREACH网络不可达 ENOTSOCK文件描述符不是一个socket ETIMEDOUT连接超时 2.2.6 连接目标网络服务器connect() 2.2.7 写入数据write() 如图7-5所示,当服务器端在接收到一个客户端的连接 后,可以通过套接字描述符进行数据的写入操作。对套接字 进行写入的形式和过程与普通文件的操作方式一致,内核会 根据文件描述符的值来查找所对应的属性,当为套接字的时 候,会
13、调用相对应的内核函数。 下面是一个向套接字文件描述符中写入数据的例子,将 缓冲区data的数据全部写入套接字文件描述符s中,返回值 为成功写入的数据长度。 int size ; char data1024; size = write(s, data, 1024); 2.2.8 读取数据read() 与写入数据类似,使用read()函数可以从套接字描述符 中读取数据。当然在读取数据之前,必须建立套接字并连接 。读取数据的方式如下所示,从套接字描述符s中读取1024 个字节,放入缓冲区data中,size变量的值为成功读取的数 据大小。 int size ; char data1024; size
14、 = read(s, data, 1024); 2.2.9 关闭套接字close() 关闭socket连接可以使用函数close()实现,函数的作 用是关闭已经打开的socket连接,内核会释放相关的资源, 关闭套接字之后不能使用这个套接字文件描述符进行读写操 作了。函数原型在第3章中已经介绍过。 函数shutdown(),可以使用更多方式来关闭连接,允 许单方向切断通信或者切断双方的通信。函数原型如下,第 一个参数s,此参数是切断通信的套接口文件描述符,第二 个参数how,此参数表示切断的方式。 #include int shutdown(int s, int how); 2.2.9 关闭套
15、接字close() 值含义 EBADF文件描述符不是合法的值 ENOTCONNsocket没有连接 ENOTSOCKs是一个文件,不是socket。 2.3 服务器/客户端的简单例子 前面几节对网络程序设计的函数进行了介绍,本节介绍 一个简单的基于TCP协议的服务器/客户端的例子,通过本 例中代码和程序构建过程的了解,读者能够对基于TCP协议 的服务器、客户端程序设计方法和过程有基本的了解,进一 步能够编写自己的程序。 2.3.1 例子功能描述 例子程序分为服务器端和客户端,客户端连接服务器后 从标准输入读取输入的字符串,发送给服务器;服务器接收 到字符串后,发送接收到的总字符串个数给客户端;
16、客户端 将接收到的服务器的信息打印到标准输出。 2.3.2 服务器网络程序 程序的代码如下,程序按照网络流程建立套接字、初始 化绑定网络地址、将套接字与网络地址绑定、设置侦听队列 长度、接收客户端连接、收发数据、关闭套接字。 1初始化工作 2建立套接字 3设置服务器地址 4绑定地址到套接字描述符 5设置侦听队列 6主循环过程 2.3.3 服务器读取和显示字符串 服务器端对客户端连接的处理过程如下,先读取从客户 端发送来的数据,然后将接收到的数据个数发送给客户端。 void process_conn_server(int s) ssize_t size = 0; char buffer1024; for(;) size = read(s, buffer, 1024); if(size = 0) return; sprintf(buffer, “%d bytes altogethern“, size); write(s, buffer, strlen(buffer)+1); 2.3.4 客户端的网