天津科技大学嵌入式操作系统-第6章 嵌入式linux网络编程_第1页
天津科技大学嵌入式操作系统-第6章 嵌入式linux网络编程_第2页
天津科技大学嵌入式操作系统-第6章 嵌入式linux网络编程_第3页
天津科技大学嵌入式操作系统-第6章 嵌入式linux网络编程_第4页
天津科技大学嵌入式操作系统-第6章 嵌入式linux网络编程_第5页
已阅读5页,还剩82页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1第6章嵌入式Linux网络编程6.1网络协议概述//6.2网路编程基础6.3TCP通信编程6.4UDP通信编程26.1网络协议概述6.1.1网络协议参考模型6.1.2TCP/IP协议族6.1.3TCP和UDP36.1.1网络协议参考模型国际标准组织(ISO)制定了OSI模型,它分为7个层次:应用层、表示层、会话层、传输层、网络层、数据链路层及物理层。1至4层被认为是底层,这些层与数据移动密切相关。5至7层是高层,包含应用程序级的数据。每一层负责一项具体的工作,然后把数据传送到下一层。4TCP/IP协议则将OSI的7层模型简化为了4层6.1.1网络协议参考模型5网络接口层:负责将二进制流转换为数据帧,并进行数据帧的发送和接收。数据帧是网络信息传输的基本单元。网络层:负责将数据帧封装成IP数据报,同时负责选择数据报的路径,即路由。传输层:负责端到端之间的通信会话连接与建立。传输协议的选择根据数据传输方式而定。应用层:负责应用程序的网络访问,这里通过端口号来识别各个不同的进程。6.1.1网络协议参考模型66.1.2TCP/IP协议族TCP/IP协议实际是一个协议族,为网际数据通信提供不同层次的通路。可将TCP/IP协议族上分为三部分网络层、传输层协议、应用程序

7第一部分也称为网络层。主要包括Internet协议(IP)、网际控制报文协议(ICMP)和地址识别协议(ARP)。Internet协议(IP):该协议被设计成互联分组交换通信网,以形成一个网际通信环境,它负责在源主机和目的地主机之间传输来自其较高层软件的称为数据报文的数据块,它在源和目的地之间提供非连接型传递服务。网际控制报文协议(ICMP):它实际上不是IP层部分,但直接同IP层一起工作,报告网络上的某些出错情况。允许网际路由器传输差错信息或测试报文。地址识别协议(ARP):ARP实际上不是网络层部分,它处于IP和数据链路层之间,它是在32位IP地址和48位局域网地址之间执行翻译的协议。6.1.2TCP/IP协议族8第二部分是传输层协议。主要包括传输控制协议和用户数据报文协议。传输控制协议(TCP):由于IP提供非连接型传递服务,因此TCP应为应用程序存取网络创造了条件,使用可靠的面向连接的传输层服务。该协议为建立网际上用户进程之间的对话负责。此外,还确保两个以上进程之间的可靠通信。它所提供的功能包括:监听输入对话建立请求、请求另一网络站点对话、可靠的发送和接收数据和适度的关闭对话。用户数据报文协议(UDP):UDP提供不可靠的非连接型传输层服务,它允许在源和目的地站点之间传送数据,而不必在传送数据之前建立对话。此外,该协议还不使用TCP使用的端对端差错校验。当使用UDP时,传输层功能全都发回,而开销却比较低。它主要用于那些不要求TCP协议的非连接型的应用程序。例如,名字服务和网络管理。第三部分是应用程序部分。这部分包括Telnet,文件传送协议(FTP和TFTP),简单的文件传送协议(SMTP)和域名服务(DNS)等协议。96.1.3TCP和UDP1.TCP协议TCP协议处于传输层,实现了从一个应用程序到另一个应用程序的数据传递。应用程序通过目的地址和端口号来区分接收数据的不同应用程序。(1)TCP数据包格式106.1.3TCP和UDP11源端口和目的端口字段:各占2字节。端口是传输层与应用层的服务接口。传输层的复用和分用功能都要通过端口才能实现。

序号字段:占4字节。TCP连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号。确认号字段:占4字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。HLEN字段:占4bit,它指出首部长度,单位为32bit字(4字节)。正常的TCP首部长度是20字节。6.1.3TCP和UDP126个标志字段:占6bit。紧急比特URG:当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。确认比特ACK:只有当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。推送比特PSH(PuSH):接收TCP收到推送比特置1的报文段,就尽快地交付给接收应用进程,而不再等到整个缓存都填满了后再向上交付。复位比特RST(ReSeT):当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。同步比特SYN:同步比特SYN置为1,就表示这是一个连接请求或连接接受报文。终止比特FIN(FINal):用来释放一个连接。当FIN=1时,表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。13窗口字段:占2字节。窗口字段用来控制对方发送的数据量,单位为字节。TCP连接的一端根据设置的缓存空间大小确定自己的接收窗口大小,然后通知对方以确定对方的发送窗口的上限。校验和:

占2字节。检验和字段检验的范围包括首部和数据这两部分。在计算校验和时,要在TCP报文段的前面加上12字节的伪首部。紧急指针字段:

占16bit。紧急指针指出在本报文段中的紧急数据的最后一个字节的序号。

6.1.3TCP和UDP14(2)TCP连接建立的过程TCP协议通过三次握手来初始化,目的是使数据段的发送和接收同步,告诉其他主机其一次可接收的数据量,并建立连接。TCP连接的建立是通过三次握手实现的。需要连接的双方发送自己的同步SYN信息给对方,在SYN中包含了末端初始的数据序号,并且需要收到对方对自身发出SYN的确认。一个典型的TCP连接建立的过程如图

15第一步客户机向服务器发送一个TCP数据包,表示请求建立连接。为此,客户端将数据包的SYN位设置为1,并且设置序列号seq=1000(我们假设为1000)。第二步服务器收到了数据包,并从SYN位为1知道这是一个建立请求的连接。于是服务器也向客户端发送一个TCP数据包。因为是响应客户机的请求,于是服务器设置ACK为1,ack_seq=1001(对方的序列号1000+1)同时设置自己的序列号。seq=2000(我们假设为2000)。第三步客户机收到了服务器的TCP,并从ACK为1和ack_seq=1001知道是从服务器来的确认信息。于是客户机也向服务器发送确认信息。客户机设置ACK=1,和ack_seq=2001(对方的序列号2000+1),seq=1001,发送给服务器。至此客户端完成连接。最后一步服务器收到确认信息,也完成连接。接下来就可以在两台主机间传输数据了。16(3)TCP连接的标识TCP是实现两主机间进程的通信,所以只有两个主机的IP地址是不能标识一条连接的,在TCP中,使用两个socket来标识一条连接。Socket由本地的IP地址和进程使用的端口号组成,<本地IP地址,本地端口>。这样对于一条TCP连接,就可以使用两个一元组来表示为“<本地IP地址,本地端口>,<远端IP地址,远端端口>”或者使用一个四元组来表示为“<本地IP地址,本地端口,远端IP地址,远端端口>”。因此,许多的网络应用只使用一个熟知端口,却可以同时支持多个用户的连接。6.1.3TCP和UDP17(4)关闭TCP连接关闭一条TCP连接有三种可能的情况:发起连接的一方(主机A)请求关闭TCP连接。在这种情况下,主机A发送的数据包中将包含一个FIN(FINISH,终止信息)控制信息,然后主机A将进入FIN—WAIT等待状态。在这种状态下,主机A仍然可以接收数据,但不能发送数据了。当连接的另一端(主机B)收到这个数据包后,将发送一个确认数据包,但可能不包括FIN控制信息,直到主机B完成任务,才发送FIN。然后A端向B端发送FIN确认信息,这时B端变为CLOSED状态,A端延时一段时间后也转换为CLOSED状态。主机B主动请求关闭TCP连接。主机A从网络中收到来自主机B的FIN控制信息,主机A将通知一层应用连接即将断开,而后当来自B端的数据发送完毕后,向B端发送FIN控制信息,确认后即断开连接。如果当主机B上层应用的超时定时器超时后,还没有收到主机A的确认信息,则将主动断开TCP连接。主机A和主机B同时发起断开连接的请求,其过程与上述两种情况大致相同,就不再详细介绍了。182.UDP协议6.1.3TCP和UDP19源端口号(16位):UDP数据包的发送方使用的端口号。目标端口号(16位):UDP数据包的接收方使用的端口号。UDP协议使用端口号为不同的应用保留其各自的数据传输通道。UDP和rap协议正是采用这一机制,实现对同一时刻内多项应用同时发送和接收数据的支持。数据包长度(16位)。数据包的长度是指包括包头和数据部分在内的总的字节数。理论上,包含包头在内的数据包的最大长度为65535字节。不过,一些实际应用往往会限制数据包的大小,有时会降低到8192字节。校验值(16位)。UDP协议使用包头中的校验值来保证数据的安全。6.1.3TCP和UDP重点回顾SQLite的API函数打开数据库关闭数据库执行函数释放内存函数显示错误信息获取结果集释放结果集等20重点回顾网络协议概述网络协议参考模型TCP/IP协议族TCP和UDP21226.2网路编程基础6.2.1socket概述6.2.2基本数据结构和函数6.2.3socket基础编程236.2.1socket概述Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学习Internet上的TCP/IP网络编程,必须理解Socket接口。Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。24常见的socket有以下三种类型。流式socket(SOCK_STREAM):流式socket提供可靠的、面向连接的通信流。它使用TCP协议,从而保证了数据传输的正确性。数据报socket(SOCK_DGRAM):数据报socket定义了一种无连接的服务,它使用UDP协议,通过相互独立的数据报传输数据,协议本身不保证传输的可靠性和数据的原始顺序。原始socket:原始socket允许对底层协议如IP,进行直接访问,它的功能强大,用户可以通过该socket开发自己的协议。6.2.1socket概述256.2.2基本数据结构和函数1.网络地址2.IP地址转换3.IP和域名的转换4.字节顺序转换5.服务信息函数261.网络地址网络地址的表示主要通过两个重要的数据类型:结构体sockaddr和sockaddr_in,其定义分别如下。(1)结构体sockaddrstructsockaddr{unsignedshortsa_family;/*地址族,AF_xxx*/

charsa_data[14];/*14字节的协议地址*/};27各个字段的含义如下:sa_family:一般为AF_INET,代表Internet(TCP/IP)地址族的IPV4协议,其他的值请查阅相关手册。sa_data:包含了一些远程计算机的IP地址、端口号和套接字的数目,这些数据是混杂在一起的。1.网络地址28(2)结构体sockaddr_in

structsockaddr_in{shortintsin_family; /*地址族*/unsignedshortintsin_port; /*端口号*/structin_addrsin_addr /*IP地址*/unsignedcharsin_zero[8]; /*填充0以保持*/ /*与structsockaddr同样大小*/};1.网络地址struct

in_addr

{

union

{

struct

{

u_char

s_b1,s_b2,s_b3,s_b4;

}

S_un_b;

struct

{

u_short

s_w1,s_w2;

}

S_un_w;

u_long

S_addr;

}

S_un;292.IP地址转换在网络上面我们用的IP都是点分十进制的格式,如:“”,而在structin_addr结构中用的是32位的IP,对应32位的IP为C0A80001。为了进行转换可以使用下面两个函数。

intinet_aton(constchar*cp,structin_addr*inp)char*inet_ntoa(structin_addrin)函数里面a代表ascii,n代表network。第一个函数表示将a.b.c.d的IP转换为32位的IP,存储在inp指针里面。第二个是将32位IP转换为a.b.c.d的格式。303.IP和域名的转换在实际中IP地址是很难记忆的,通常都是借助DNS服务,比如:“”,但是这个名字怎样转换为IP地址呢?我们可以使用gethostbyname()函数和gethostbyaddr(),这两个函数定义如下structhostent*gethostbyname(constchar*hostname);structhostent*gethostbyaddr(constchar*addr,intlen,inttype);31这两个函数返回了一个指向structhostent的指针,这个structhostent定义如下:

structhostent{char*h_name; /*主机的正式名称*/char**h_aliases; /*主机的别名列表*/inth_addrtype; /*主机的地址类型AF_INET*/inth_length;/*主机的地址长度对于IP4是4字节32位*/

char**h_addr_list; /*主机的IP地址列表*/};#defineh_addrh_addr_list[0]/*主机的第一个IP地址*/gethostbyname()函数可以将机器名转换为一个结构指针。在这个结构里面储存了域名的信息。gethostbyaddr()函数可以将一个32位的IP地址(C0A80001)转换为结构指针。这两个函数失败时返回NULL且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息。

324.字节顺序转换在Linux下面,有专门的字节转换函数:unsignedlonginthtonl(unsignedlonginthostlong);unsignedshortinthtons(unisgnedshortinthostshort);unsignedlongintntohl(unsignedlongintnetlong);

unsignedshortintntohs(unsignedshortintnetshort);

在这四个转换函数中,h代表host,n代表network。s代表short,l代表long。第一个函数的意义是将本机器上的long数据转化为网络上的long。其他几个函数的意义也差不多。335.服务信息函数在网络程序里面我们有时候需要知道端口、IP和服务信息。

intgetsockname(intsockfd,structsockaddr*localaddr,int*addrlen);intgetpeername(intsockfd,structsockaddr*peeraddr,int*addrlen);structservent*getservbyname(constchar*servname,constchar*protoname);

structservent*getservbyport(intport,constchar*protoname);structservent

{

char*s_name; /*正式服务名*/

char**s_aliases; /*别名列表*/

ints_port; /*端口号*/

char*s_proto; /*使用的协议*/}

346.2.3socket基础编程针对以上函数,我们给出一个实际例子来说明如何使用这些函数。在这个例子里面,为了判断用户输入的是IP还是域名我们调用了两个函数,第一次我们假设输入的是IP所以调用inet_aton,

失败的时候,再调用gethostbyname而得到信息。其源代码如下:

35/*******(hostname_ip.c)************/#include"stdio.h"#include"stdlib.h"#include"errno.h"#include"sys/types.h"#include"sys/socket.h"#include"unistd.h"#include"netinet/in.h"#include"netdb.h"intmain(intargc,char**argv){

structsockaddr_inaddr;

structhostent*host;

char**alias;

if(argc<2) { fprintf(stderr,"Usage:%shostname|ip..\n\a",argv[0]); exit(1); }36argv++;for(;*argv!=NULL;argv++){if(inet_aton(*argv,&addr.sin_addr)!=0){

host=gethostbyaddr((char*)&addr.sin_addr,4,AF_INET);

printf("AddressinformationofIp%s\n",*argv);

}else{

host=gethostbyname(*argv);printf("Addressinformationofhost%s\n",*argv);}

if(host==NULL){fprintf(stderr,"Noaddressinformationof%s\n",*argv);

continue;

}6.2.3socket基础编程37

printf("Officialhostname%s\n",host->h_name);

printf("Namealiases:");

for(alias=host->h_aliases;*alias!=NULL;alias++)

printf("%s,",*alias);

printf("\nIpaddress:");for(alias=host->h_addr_list;*alias!=NULL;alias++)printf("%s,",inet_ntoa(*(structin_addr*)(*alias)));}}6.2.3socket基础编程38程序的编译调试过程命令如下:[root@JLUZHhostname]#gcchostname_ip.c-ohostname_ip[root@JLUZHhostname]#lshostname_iphostname_ip.c

由上可知,生成了一个hostname_ip可执行文件,分别使用ip和机器名作为参数进行测试,为了测试的方便,这里使用地址,当然也可以根据实际机器的ip进行测试。其运行结果如下:6.2.3socket基础编程39[root@JLUZHhostname]#./hostname_ipAddressinformationofIpOfficialhostnameJLUZHNamealiases:localhost.localdomain,localhost,Ipaddress:,[root@JLUZHhostname]#./hostname_ipJLUZHAddressinformationofhostJLUZHOfficialhostnameJLUZHNamealiases:localhost.localdomain,localhost,Ipaddress:,406.3TCP通信编程6.3.1TCP通信过程6.3.2TCPServer程序设计6.3.3TCPClient程序设计6.3.4TCP程序测试过程416.3.1TCP通信过程42结合以上步骤来说明各个函数的用法:1.使用socket()创建套接字为了建立socket,程序可以调用socket函数,该函数返回一个类似于文件描述符的句柄。所需头文件:#include<sys/types.h>#include<sys/socket.h>函数原型:intsocket(intfamily,inttype,intprotocol)socket()系统调用创建一个用于网络通信的套接字,并返回该套接字的整数描述符。6.3.1TCP通信过程43其参数含义如下所示。参数family:表示协议或地址族,对于TCP/IP通常为AF_INET,与socket_addr中的sin_family的含义和取值是一样的。参数type:表示套接字的类型,对于TCP为SOCK_STREAM,对于UDP为SOCK_DGRAM,对于原始套接字为SOCK_RAW。参数protocol:表示使用的协议号,用0指定family和type的默认协议号。1.使用socket()创建套接字44Socket()调用返回一个整型socket描述符,否则返回-1。并设置errno为下列这些值。查看errno可知道出错的详细情况。EPROTONOSUPPORT:错误原因是参数中的错误,表示申请的服务或指定的协议无效;EMFILE:错误原因是应用程序的描述符表已经满;ENFILE:错误原因是内部的系统文件表已满;ENOBUFS:错误原因是系统没有可用的缓冲空间。1.使用socket()创建套接字45调用Socket函数时,socket执行体将建立一个Socket,实际上“建立一个Socket”意味着为一个Socket数据结构分配存储空间,你可以在后面的调用使用它。Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。Socket执行体为你管理描述符表。两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。Socket数据结构中包含这五种信息。1.使用socket()创建套接字46通过socket调用返回一个socket描述符后,在使用socket进行网络传输以前,必须配置该socket。面向连接的socket客户端通过调用Connect函数在socket数据结构中保存本地和远端信息。无连接socket的客户端和服务端以及面向连接socket的服务端通过调用bind函数来配置本地信息。Bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。使用bind()函数,绑定套接字到一个IP地址和一个端口上。所需头文件:#include<sys/types.h>#include<sys/socket.h>2.绑定本地地址47函数原型:intbind(intsockfd,structsockaddr*sa,intaddrlen)参数说明如下:参数sockfd:是由socket()函数返回的套接字描述符;参数sa:是一个指向structsockaddr的指针,包含有关地址的信息,如名称、端口和IP地址。参数addrlen:是套接字地址接口的长度,可以设置为sizeof(structsockaddr)。2.绑定本地地址48使用listen()函数将套接字设置为监听模式,以等待连接请求。所需头文件:#include<sys/socket.h>函数原型:intlisten(intsockfd,intbacklog)当调用bind()函数,将一个套接字绑定到某个端口上之后,就可以通过调用Listen()函数来准备接受客户端提出的连接请求了。其参数含义如下:参数sockfd:是一个套接字描述符,由socket()系统调用获得。参数backlog:是未经过处理的连接请求队列可以容纳的最大数目。3.listen()函数49请求到来后,使用accept()函数接受连接请求。所需头文件:#include<sys/socket.h>函数原型:intaccept(intsockfd,structsockaddr*addr,int*addrlen)当服务器执行了listen()调用后,一般就使用accept()函数来响应连接请求,建立连接并产生一个新的socket描述符来描述该连接,该连接用来与特定的客户端交换信息。各参数意义如下。参数sockfd:是正在侦听的一个套接字描述符。参数addr:一般是一个指向structsockaddr_in结构的指针,将在accept()函数调用返回后填入远程连接过来的计算机的信息,比如远程计算机的IP地址和端口。参数addrlen:表示参数addr所占的内存区的大小,在accept()函数调用返回后填入返回的addr结构体的大小。4.accept()函数50客户端如果需要申请一个连接,必须调用connect()函数,这个函数的任务就是建立与服务器的连接。所需头文件:#include<sys/types.h>#include<sys/socket.h>函数原型:intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen);它的三个参数意义如下:参数sockfd:套接字文件描述符,由socket()函数返回;参数serv_addr:是一个存储远程计算机的IP地址和端口信息的sockaddr结构;参数addrlen:是serv_addr结构体所占的内存大小。5.connect()函数51connect()调用成功就返回0,如果返回一1,则代表发生了错误。当错误发生时,全局变量errno含有下面的值。

EBADF:错误原因是参数socket未指定一个合法的描述符;ENOTSOCK:错误原因是参数socket未指定一个套接字描述符;EAFNOSUPPORT:错误原因是远程端点指定的地址族不能与这种类型的套接字一起使用;EADDRNOTAVAIL:错误原因是指定的地址不可用;EISCONN:错误原因是套接字已被连接;ETIMEDOUT:错误原因是(只用于TCP)协议因未成功建立一个连接而超时;ECONNREFUSED:错误原因是(只用于TCP)连接被远程机器拒绝;52ENETUNREACH:错误原因是(只用于TCP)网络当前不可到达;EADDRINUSE:错误原因是指定的地址正在使用;EINPROGRESS:错误原因是(只用于TCP)套接字是非阻塞的,且一个连接尝试将被阻塞;EALREADY:错误原因是(只用于TCP)套接字是非阻塞的,且调用将等待前一个连接尝试完成。当connect()调用成功之后,就可以使用sockfd作为与服务器连接的套接字描述符,接下来就可以使用I/O函数,如read()、write()、send()、recv()等进行数据传输了。5.connect()函数53一旦成功建立起TCP连接,得到了一个socket,剩下要做的就是数据通信了。由于socket的本质就是文件描述符,因此,凡是基于文件描述符的I/O函数几乎都可以用于数据通信,如read()、write()、put()、get()等。下面介绍几个基本的函数。

6.数据通信54这两个函数是最基本的,通过连接的套接字流进行通信的函数。与write()和read()函数的功能很相似,只是其参数设置更容易对套接字进行读写操作控制。其原型如下:所需头文件:#include<sys/types.h>#include<sys/socket.h>函数原型:intsend(intsockfd,constvoid*msg,intlen,intflags);intrecv(intsockfd,void*buf,intlen,unsignedintflags);(1)send()和recv()函数55它们的参数很相似,因此就统一说明如下。参数sockfd:是代表与远程程序连接的套接字描述符;参数msg:是一个指针,指向想发送或存储信息的地址;参数len:是信息的长度;参数flags:发送或接收标记,一般都设为0。具体的设置可以参考相关的man手册。(1)send()和recv()函数56这两个函数是进行无连接的UDP通信时使用的。使用这两个函数,则数据会在没有建立过任何连接的网络上传输。所需头文件:#include<sys/types.h>#include<sys/socket.h>函数原型:intsendto(intsockfd,constvoid*msg,intlen,unsignedintflags,conststructsockaddr*to,inttolen);intrecvfrom(intsockfd,void*buf,intlen,unsignedintflagsstructsockaddr*from,int*fromlen);(2)sendto()和recvfrom()函数57sendto()函数和send()函数基本一致。参数sockfd:是代表与远程程序连接的套接字描述符;参数msg:是一个指针,指向欲发送的信息的地址;参数len:是欲发送信息的长度;参数flags:发送标记,一般都设为0。与send()函数中的flags参数一致:参数to:是一个指向structsockaddr结构的指针,里面包含了远程主机的IP地址和端口数据;参数tolen:指出了参数to在内存中的大小。(2)sendto()和recvfrom()函数58recvfrom()的参数含义如下所示。参数sockfd:是要读取数据的套接字描述符;参数buf:是一个指针,指向存储数据的内存缓存区域:参数len:是缓存区的最大尺寸:参数flags:是recvO函数的标志,一般都为0;参数from:指向一个structsockaddr的结构,里面存有远程的IP地址和端口数;参数fromlen:当函数返回时,fromlen指向的数据是参数from所指的结构所占的内存大小。(2)sendto()和recvfrom()函数59所需头文件:#include<unistd.h>函数原型:intclose(intsockfd)当程序进行网络传输完毕后,就应该关闭这个套接字描述符所表示的连接。实现这步非常简单,执行close()之后,套接字将不会再允许进行读操作和写操作。任何有关对套接字描述符进行读和写的操作都会接收到一个错误。7.使用close()函数关闭当前的连接重点回顾Socket:具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。6061重点回顾TCP通信过程626.3.2TCPServer程序设计/**tcpserver.c*******/#include<stdlib.h>#include<stdio.h>#include<errno.h>#include<string.h>#include<netdb.h>#include<sys/types.h>#include<netinet/in.h>#include<sys/socket.h>intmain(intargc,char*argv[]){ intsockfd,new_fd; structsockaddr_inserver_addr;structsockaddr_inclient_addr; intsin_size,portnumber; constcharhello[]="Hello!\n";6.3.2TCPServer程序设计if(argc!=2) { fprintf(stderr,"Usage:%sportnumber\a\n",argv[0]);exit(1); } if((portnumber=atoi(argv[1]))<0){fprintf(stderr,"Usage:%sportnumber\a\n",argv[0]);exit(1); }

/*服务器端开始建立socket描述符*/if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)//TCP{fprintf(stderr,"Socketerror:%sportnumber\n",strerror(errno));

exit(1); }636.3.2TCPServer程序设计printf("Socketidis%d\n",sockfd);/*服务器端填充sockaddr结bzero(&server_addr,sizeof(structsockaddr_in));server_addr.sin_family=AF_INET;//IPV4 server_addr.sin_addr.s_addr=htonl(INADDR_ANY); //locallongconverttonetworklongserver_addr.sin_port=htons(portnumber); //localshortconverttonetworkshort646.3.2TCPServer程序设计/*捆绑sockfd描述符*/

if(bind(sockfd,(structsockaddr*)(&server_addr),sizeof(structsockaddr))==-1){ fprintf(stderr,"Binderror:%s\n\a",strerror(errno));exit(1); } printf("Bind\n");

/*监听sockfd描述符*/

if((listen(sockfd,5))==-1){ fprintf(stderr,"Listenerror:%s\n\a",strerror(errno));exit(1); } printf("Listen\n"); 65while(1){ //tryuntilconnect sin_size=sizeof(structsockaddr_in);/*监听sockfd描述符*/

if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),&sin_size))==-1){ fprintf(stderr,"Accepterror:%s\n\a",strerror(errno));exit(1); } printf("Accept!\n"); fprintf(stderr,"Servergetconnectionfrom%s\n",inet_ntoa(client_addr.sin_addr));if(send(new_fd,hello,strlen(hello),0)==-1){fprintf(stderr,"SendError:%s\n",strerror(errno));exit(1); }/*这个通信已经结束*/ close(new_fd); }/*循环下一个*/ close(sockfd); exit(0); }66676.3.3TCPClient程序设计/***tcpclient.c#include<stdlib.h>#include<stdio.h>#include<errno.h>#include<string.h>#include<netdb.h>#include<sys/types.h>#include<netinet/in.h>#include<sys/socket.h> intmain(intargc,char*argv[]){ intsockfd; charbuffer[1024]; structsockaddr_inserver_addr;structhostent*host; intportnumber,nbytes;686.3.3TCPClient程序设计if(argc!=3){ fprintf(stderr,"Usage:%shostnameportnumber\a\n",argv[0]);exit(1); } if((host=gethostbyname(argv[1]))==NULL){ fprintf(stderr,"Hostnameerror:%s\a\n",strerror(errno)); exit(1); } portnumber=atoi(argv[2]); if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){fprintf(stderr,"Socketerror:%s\a\n",strerror(errno));exit(1); } printf("Socketidis%d\n",sockfd);696.3.3TCPClient程序设计server_addr.sin_family=AF_INET;server_addr.sin_port=htons(portnumber);server_addr.sin_addr=*((structin_addr*)host->h_addr);printf("Officialhostname%s.\nTheportnumberis%d\n",host->h_name,portnumber);/*客户程序发起连接请求*/if(connect(sockfd,(structsockaddr*)(&server_addr),sizeof(structsockaddr))==-1){ fprintf(stderr,"ConnectError:%s\n\a",strerror(errno));exit(1); } printf("Connect\n");706.3.3TCPClient程序设计/*连接成功了*/if((nbytes=recv(sockfd,buffer,1024,0))==-1){ fprintf(stderr,"ReadError:%s\n\a",strerror(errno));exit(1); } buffer[nbytes]='\0'; printf("Ihavereceived:%s\n",buffer);/*结束通信*/close(sockfd); exit(0);}716.3.4TCP程序测试过程分别将服务器端程序编译为tcpserver,客户端程序编译为tcpclien,在虚拟机上运行,测试时使用localhost即IP地址为,进行这个测试,需要打开两个终端,步骤如下:1.首先终端一运行tcpserver。[root@JLUZHtcp]#./tcpserver20002.在另一终端运行客户端。[root@JLUZHtcp]#./tcpclientlocalhost2000726.3.4TCP程序测试过程此时,在终端一会显示:“Servergetconnectionfrom”,表示tcpserver程序收到了从来的连接信息。在终端二会显示:“Ihavereceived:Hello”,表示tcpclient程序收到了从服务器端发送来的信息。注意:地址localhost是本地循环地址,它代表本机的IP地址,用十分点数字表示为:。这个地址是一个特殊的IP地址,通常用来测试IP协议是否工作正常,在本地调试网络程序时将会经常接触到这个地址。736.4UDP通信编程6.4.1UDP通信过程6.4.2UDP服务器端程序设计6.4.3UDP客户端程序设计6.4.4UDP程序测试过程746.4.1UDP通信过程UDP通信的基本过程如下:在服务器端,服务器首先创建一个UDP数据报类型的套接字,然后服务器就调用bind()函数,给此UDP套接字绑定一个端口。由于不需要建立连接,因此服务器端就可以通过调用recvfrom()函数在指定的端口上等待客户端发送来的UDP数据报。在客户端,同样要先通过socket()函数创建一个数据报套接字,然后有操作系统为这个套接字来分配端口号。此后客户端就可以使用sendto()函数向一个指定的地址发送一个UDP数据报。服务器端接收到套接字后,从recvfrom()中返回,在对数据报进行处理之后,再用sendto()函数将处理的结果返回客户端。756.4.1UDP通信过程766.4.2UDP服务器端程序设计编写UDPServer程序的步骤如下:

1.使用socket()来建立一个UDPsocket,第二个参数为SOCK_DGRAM。2.初始化sockaddr_in结构的变量,并赋值。这里使用“8888”作为服务程序的端口,使用“INADDR_ANY”作为绑定的IP地址即任何主机上的地址。3.使用bind()把上面的socket和定义的IP地址和端口绑定。这里检查bind()是否执行成功,如果有错误就退出。这样可以防止服务程序重复运行的问题。4.进入无限循环程序,使用recvfrom()进入等待状态,直到接收到客户程序发送的数据,就处理收到的数据,并向客户程序发送反馈。77/**********udpserver.c***********/#include<sys/types.h>#include<sys/socket.h>#include<string.h>#include<netinet/in.h>#include<stdio.h>#include<stdlib.h>6.4.2UDP服务器端程序设计78voiddo_echo(intsockfd,structsockaddr*pcliaddr,socklen_tclilen){intn; socklen_tlen;charmesg[80];for(;;){len=clilen;n=recvfrom(sockfd,mesg,80,0,pcliaddr,&len); /*等待接收数据*/sendto(sockfd,mesg,n,0,pcliaddr,len); /*将接收到的数据发送回去*/mesg[n]=0;fputs(mesg,stdout);}}79intmain(void){intsockfd;structsockaddr_inservaddr,cliaddr;sockfd=socket(AF_INET,SOCK_DGRAM,0);/*创建socket*/bzero(&servaddr,sizeof(servaddr));/*初始化servaddr*/servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=htonl(INADDR_ANY); /*定义为INADDR_ANY*/servaddr.sin_port=htons(8888);if(bind(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr))==-1)/*绑定IP地址和端口*/{perror("binderror");exit(1);}do_echo(sockfd,(structsockaddr*)&cliaddr,sizeof(cliaddr));return0;}806.4.3UDP客户端程序设计客户端主要需要完成与服务器请求数据、应答数据等工作,与TCP通信的客户端不同的是UDP通信的客户端不需要与服务器端建立连接,只要将数据报准备好,通过线路发出即可,因此UDP通信的客户端是不知道服务端是否正确地收到了发出的数据,除非程序员自己定义应用层的通信协议,让服务器端反馈应答信息,这样才能保证通信的可靠性

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论