《嵌入式软件设计概述 (12).ppt》由会员分享,可在线阅读,更多相关《嵌入式软件设计概述 (12).ppt(72页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、套接字简介套接字简介套接字是一种进程间通信的方法,不同于以往介绍的进程间通信方法的是,它并不局限于同一台计算机的资源,例如共享内容或者消息队列。套接字接口(socket interface)由伯克利版本UNIX引入,可以认为是对管道概念的扩展一台机器上的进程可以使用套接字与另一台机器上的进程通信。因此客户与服务器可以分散到网络中。同一台机器的进程间也可以用套接字通信。套套接接字概念字概念Windows对套接字的支持对套接字的支持微软的windows系统也实现了套接字接口,因此windows程序可以通过网络和Linux/UNIX计算机进行通信,实现客户/服务器系统。首先,服务器应用程序通过soc
2、ket系统调用创建一个套接字创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,不能与其他进程共享。其次,服务器进程使用bind系统调用给套接字命名给套接字命名。本地套接字本地套接字的名字是linux文件系统的文件名,一般将其放在/tmp或者/usr/tmp目录下。网络套接字网络套接字的名字是与客户相连接的特定网络有关的服务标识符。此标识符允许linux将进入的针对特定端口号的连接转到正确的服务器进程。套套接接字工作过程(服务器端)字工作过程(服务器端)套接字工作过程(服务器端续)套接字工作过程(服务器端续)接下来,服务器进程开始等待客户连接到这个命名套接字,调用listen创建
3、一个等待队列,以便存放来自客户的进入连接。最后,服务器通过accept系统调用来接受客户的连接接受客户的连接。此时,会产生一个与原有的命名套接字不同的新套接字,它仅用于与这个特定的客户通信,而命名套接字则被保留下来继续处理来自其他客户的连接。套接字工作过程(客户端)套接字工作过程(客户端)一旦建立了连接,就可以像使用底层文件描述符那样来用套接字进行双向的双向的数据通信。数据通信。调用socket创建一个未命名套接字创建一个未命名套接字,将服务器的命名套接字作为一个地址来调用connect与服务器建立连接。套接字的特性由三个属性决定:域(domain)类型(type)协议(protocol)套接
4、字属性套接字属性域域域(domain):指定套接字通信中使用的网络介质,包括地址格式。最常用的有以下两种:AF_INETAF_INET,即互联网络,基于IP协议,并且每个服务对应一个端口号,套接字地址由IP地址+端口号决定;AF_UNIXAF_UNIX,基于本地机器,底层协议使用文件输入/输出,地址为绝对路径的文件名。类型类型一个套接字域可能有不同的通信方式,每种通信方式有不同的特性。AF_UNIXAF_UNIX域的套接字没有通信方面的问题,因为其基于文件系统,提供了一个可靠的双向通信路径。类型类型AF_INET域中,需要注意底层网络的特性。流套接字流套接字:由类型SOCK_STREAMSOC
5、K_STREAM指定,基于TCP/IP实现,提供一个有序、可靠、双向字节流的连接,发送的数据不会丢失、乱序、重复。大的消息会被分块、传输、重组,很像一个文件流。数据报套接字数据报套接字:由SOCK_DGRAMSOCK_DGRAM指定,基于UDP/IP协议,不建立和维持可靠连接,开销小,服务器崩溃不需要客户端重启,因为基于数据报的服务器不保留连接信息协议协议我们重点讨论网络套接字和文件系统套接字,不需要选择特定协议,只要默认值(0)即可。socket系统调用创建一个套接字,并返回一个描述符,该描述符可以用来访问这个套接字。创建的套接字是一条通信链路的一个端点。domain:指定域type:指定套
6、接字的通信类型protocol:指定使用的协议#include#include int socket(int domain,int type,int protocol);创建套接字创建套接字每个套接字域都有自己的地址格式。AF_UNIX:地址格式由sockaddr_un来描述AF_INET:地址格式由sockaddr_in来指定struct sockaddr_un sa_family_t sun_family;/*AF_UNIX*/char sun_path;/*pathname*/struct sockaddr_inshort int sin_family;/*AF_INET*/unsign
7、ed short int sin_port;struct in_addr sin_addrstruct in_addr unsigned long int s_addr;/四字节的IP地址套接字地址套接字地址要想让通过socket调用创建的套接字可以被其他进程使用,服务器程序必须给套接字命名,这样AF_UNIX套接字才会关联到一个文件系统的路径名上,AF_INET套接字关联到一个IP的端口上。#include int bind(int socket,const struct sockaddr*address,size_t address_len);bind调用把参数address中的地址分配给
8、文件描述符socket关联的未命名套接字,地址长度由address_len来传递。命名套接字命名套接字关于地址关于地址地址长度取决于地址族,bind调用将一个特定的地址结构指针转换为指向通用地址类型通用地址类型:structsockaddr*#include int bind(int socket,const struct sockaddr*address,size_t address_len);本地文件系统地址类型:struct sockaddr_un网络套接字地址类型:struct sockaddr_inbind的返回的返回值值bind函数调用成功返回0,失败返回-1.并把errno设置为
9、下表值。EBADF文件描述符无效ENOTSOCK文件描述符对应的不是一个套接字EINVAL文件描述符对应的是一个已命名的套接字EADDRNOTAVATL地址不可用EADDRINUSE地址已绑定了一个套接字EACCESS因权限不足,不能创建文件系统中的路径名ENOTDIR,ENAMETOOLONG表明选择的文件名不符合要求#include int listen(int socket,int backlog);为了能够在套接字上接受进入的连接,服务器程序必须创建一个队列来保持未处理的请求,它用listen系统调用来完成这一工作。当服务器正忙于处理一个客户请求时,后续的客户连接放入队列等待处理。函数
10、执行成功返回0,失败返回-1.套接字队列中,等待处理的进入连接的个数最多不能超过backlog这个数字,多出的连接请求将被拒绝,导致客户连接失败。创建套接字队列创建套接字队列#include int accept(int socket,struct sockaddr*address,size_t*address_len);一旦服务器程序创建并命名了套接字之后,就可以通过accept系统调用来等待客户建立对该套接字的连接。连接客户的地址将被放入addressaddress参数指向的sockaddr结构中。address_lenaddress_len指定客户结构的长度,如果客户地址的长度超出这个值
11、将被截断,所以必须先得到这个合适的值。接受连接接受连接关于关于accept调用调用accept函数只有当客户程序试图连接到由socket参数指定的套接字上时才返回,否则将一直阻塞。accept函数将创建一个新的套接字来与该客户通信,并且返回新套接字的描述符,新套接字的类型与服务器监听套接字一致。#include int connect(int socket,struct sockaddr*address,size_t*address_len);客户程序通过在一个未命名套接字与服务器监听套接字之间建立连接,以此来连接到服务器。参数socket指定的套接字将连接到参数address指定的服务器套接
12、字,address指向的结构长度由参数address_len指定。请求连接请求连接如果连接不能立刻建立,connect调用将阻塞一段不确定的超时时间。一旦超时时间到达,连接将被放弃,connect调用失败。应该在连接的两端都关闭套接字。可以通过close函数终止服务器和客户上的套接字连接,操作过程与对文件描述符进行关闭一样。关闭套接字关闭套接字客户端程序:创建一个未命名的套接字,然后把它连接到服务器套接字server_socket上,向服务器写一个字符,再读回经服务器处理后的一个字符。服务器端程序:首先创建一个服务器套接字,绑定到一个名字,然后创建一个监听队列,接收来自客户程序的连接。套接套接
13、字举例字举例#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_un server_address;struct sockaddr_un client_address;unlink(server_socket);server_sockfd=socket(AF_UNIX,SOCK_STREAM,0);server_address.sun_family=AF_UNIX;strcpy(server_add
14、ress.sun_path,server_socket);server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);定义套接字标识定义套接字标识符、地址长度符、地址长度例:例:服务器端程序服务器端程序server1.c#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len
15、,client_len;struct sockaddr_un server_address;struct sockaddr_un client_address;unlink(server_socket);server_sockfd=socket(AF_UNIX,SOCK_STREAM,0);server_address.sun_family=AF_UNIX;strcpy(server_address.sun_path,server_socket);server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&se
16、rver_address,server_len);listen(server_sockfd,5);例:例:服务器端程序服务器端程序server1.c套接字域地址格式,定义在sys/un.h中:structshort int sun_family;char sun_path108;#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_un server_address;struct sockaddr_u
17、n client_address;unlink(server_socket);server_sockfd=socket(AF_UNIX,SOCK_STREAM,0);server_address.sun_family=AF_UNIX;strcpy(server_address.sun_path,server_socket);server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);如果当前目录有叫做如果当前目
18、录有叫做server_socketserver_socket的文件,的文件,则删掉。则删掉。例:例:服务器端程序服务器端程序server1.c#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_un server_address;struct sockaddr_un client_address;unlink(server_socket);server_sockfd=socket(AF_UNIX,S
19、OCK_STREAM,0);server_address.sun_family=AF_UNIX;strcpy(server_address.sun_path,server_socket);server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);创建一个创建一个AF_UNIXAF_UNIX格式的格式的socketsocket,采用字节流方式,采用字节流方式,默认协议,并返回此默认协议,并返回此socketso
20、cket的文件描述符。的文件描述符。例:例:服务器端程序服务器端程序server1.c#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_un server_address;struct sockaddr_un client_address;unlink(server_socket);server_sockfd=socket(AF_UNIX,SOCK_STREAM,0);server_address
21、.sun_family=AF_UNIX;strcpy(server_address.sun_path,server_socket);server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);为此为此socket命名:命名:1、设置设置UNINX/Linux文文件系统本地件系统本地套接套接字地址字地址2、命名此套接字命名此套接字例:例:服务器端程序服务器端程序server1.c#include#include#
22、include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_un server_address;struct sockaddr_un client_address;unlink(server_socket);server_sockfd=socket(AF_UNIX,SOCK_STREAM,0);server_address.sun_family=AF_UNIX;strcpy(server_address.sun_path,server_so
23、cket);server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);创建长度为创建长度为5的监听队列的监听队列例:例:服务器端程序服务器端程序server1.cwhile(1)char ch;printf(server waitingn);client_len=sizeof(client_address);client_sockfd=accept(server_sockfd,(struct sockaddr
24、*)&client_address,&client_len);read(client_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockfd);/主函数结束等待客户端连接的到来等待客户端连接的到来例:例:服务器端程序服务器端程序server1.cwhile(1)char ch;printf(server waitingn);client_len=sizeof(client_address);client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_addre
25、ss,&client_len);read(client_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockfd);/主函数结束等待客户端连接的到来等待客户端连接的到来server1.c例:例:服务器端程序服务器端程序while(1)char ch;printf(server waitingn);client_len=sizeof(client_address);client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&client_len)
26、;read(client_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockfd);/主函数结束获取客户端套接字地址长度获取客户端套接字地址长度例:例:服务器端程序服务器端程序server1.cwhile(1)char ch;printf(server waitingn);client_len=sizeof(client_address);client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&client_len);read(clien
27、t_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockfd);/主函数结束服务器进程阻塞自身,直到有客户服务器进程阻塞自身,直到有客户端请求建立连接,此时生成一个新端请求建立连接,此时生成一个新的套接字,并返回新套接字的描述的套接字,并返回新套接字的描述符,用此新套接字与客户进行符,用此新套接字与客户进行通信通信新新套接字类型与服务器监听套接字套接字类型与服务器监听套接字类型是一样的。类型是一样的。例:例:服务器端程序服务器端程序server1.c#include#include#include#include#incl
28、ude int main()int sockfd;int len;struct sockaddr_un address;int result;char ch=A;sockfd=socket(AF_UNIX,SOCK_STREAM,0);address.sun_family=AF_UNIX;strcpy(address.sun_path,server_socket);len=sizeof(address);result=connect(sockfd,(struct sockaddr*)&address,len);变量定义变量定义例:例:客户端客户端程序程序client1.c#include#in
29、clude#include#include#include int main()int sockfd;int len;struct sockaddr_un address;int result;char ch=A;sockfd=socket(AF_UNIX,SOCK_STREAM,0);address.sun_family=AF_UNIX;strcpy(address.sun_path,server_socket);len=sizeof(address);result=connect(sockfd,(struct sockaddr*)&address,len);为客户创建一个套接字为客户创建一
30、个套接字例:例:客户端客户端程序程序client1.c#include#include#include#include#include int main()int sockfd;int len;struct sockaddr_un address;int result;char ch=A;sockfd=socket(AF_UNIX,SOCK_STREAM,0);address.sun_family=AF_UNIX;strcpy(address.sun_path,server_socket);len=sizeof(address);result=connect(sockfd,(struct so
31、ckaddr*)&address,len);根据服务器的根据服务器的情况设情况设定连接地址定连接地址例:例:客户端客户端程序程序client1.c#include#include#include#include#include int main()int sockfd;int len;struct sockaddr_un address;int result;char ch=A;sockfd=socket(AF_UNIX,SOCK_STREAM,0);address.sun_family=AF_UNIX;strcpy(address.sun_path,server_socket);len=si
32、zeof(address);result=connect(sockfd,(struct sockaddr*)&address,len);连接到服务器套接字连接到服务器套接字例:例:客户端客户端程序程序client1.cif(result=-1)perror(oops:client1);exit(1);write(sockfd,&ch,1);read(sockfd,&ch,1);printf(char from server=%cn,ch);close(sockfd);exit(0);出错则退出出错则退出例:例:客户端客户端程序程序client1.cif(result=-1)perror(oop
33、s:client1);exit(1);write(sockfd,&ch,1);read(sockfd,&ch,1);printf(char from server=%cn,ch);close(sockfd);exit(0);通过套接字文件描通过套接字文件描述符读写信息述符读写信息int main()int sockfd;int len;struct sockaddr_un address;int result;char ch=A;例:例:客户端客户端程序程序client1.c网络套接字网络套接字采用回路网络回路网络连接到一个网络套接字。回路网络只包含一台计算机,通常称为localhost,IP
34、地址为127.0.0.1,可以在/etc/hosts文件中找到。#include#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_in server_address;struct sockaddr_in client_address;server_sockfd=socket(AF_INET,SOCK_STREAM,0);server_address.sin_family=AF_INET;serve
35、r_address.sin_addr.s_addr=inet_addr(“127.0.0.1”);server_address.sin_port=9734;例:例:服务器端程序服务器端程序server2.c#include#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_in server_address;struct sockaddr_in client_address;server_sockf
36、d=socket(AF_INET,SOCK_STREAM,0);server_address.sin_family=AF_INET;server_address.sin_addr.s_addr=inet_addr(“127.0.0.1”);server_address.sin_port=9734;例:例:服务器端程序服务器端程序server2.c#include#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct so
37、ckaddr_in server_address;struct sockaddr_in client_address;server_sockfd=socket(AF_INET,SOCK_STREAM,0);server_address.sin_family=AF_INET;server_address.sin_addr.s_addr=inet_addr(“127.0.0.1”);server_address.sin_port=9734;例:例:服务器端程序服务器端程序server2.c server_len=sizeof(server_address);bind(server_sockfd,(
38、struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);while(1)char ch;printf(server waitingn);client_len=sizeof(client_address);client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&client_len);read(client_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockf
39、d);例:例:服务器端程序服务器端程序server2.c server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);while(1)char ch;printf(server waitingn);client_len=sizeof(client_address);client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&clie
40、nt_len);read(client_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockfd);例:例:服务器端程序服务器端程序server2.c server_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,server_len);listen(server_sockfd,5);while(1)char ch;printf(server waitingn);client_len=sizeof(client_
41、address);client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&client_len);read(client_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockfd);例:例:服务器端程序服务器端程序server2.c#include#include#include#include#include#include int main()int sockfd;int len;struct sockaddr_in address;/
42、地址格式int result;char ch=A;sockfd=socket(AF_INET,SOCK_STREAM,0);/生成套接字address.sin_family=AF_INET;address.sin_addr.s_addr=inet_addr(“127.0.0.1”);/格式转换例:例:客户客户端端程序程序client2.caddress.sin_port=9734;len=sizeof(address);result=connect(sockfd,(struct sockaddr*)&address,len);if(result=-1)perror(oops:client2);
43、exit(1);write(sockfd,&ch,1);read(sockfd,&ch,1);printf(char from server=%cn,ch);close(sockfd);exit(0);例:例:客户客户端端程序程序client2.caddress.sin_port=9734;len=sizeof(address);result=connect(sockfd,(struct sockaddr*)&address,len);if(result=-1)perror(oops:client2);exit(1);write(sockfd,&ch,1);read(sockfd,&ch,1)
44、;printf(char from server=%cn,ch);close(sockfd);exit(0);例:例:客户客户端端程序程序client2.c字节序问题字节序问题不同计算机的字节序不一样,例如intel机器一般采用小端方式,而motorola一般采用大端方式,因此传输信息时会造成混乱。网络字节序:客户和服务器在传输之前,将内部整数表示方式转换为网络字节序可解决字节序问题。#include unsigned long int htonl(unsigned long int hostlong);unsigned short int htons(unsigned short int h
45、ostshort);unsigned long int ntohl(unsigned long int netlong);unsigned short int ntohs(unsigned short int netshort);#include#include#include#include#include#include int main()int server_sockfd,client_sockfd;int server_len,client_len;struct sockaddr_in server_address;struct sockaddr_in client_address;
46、server_sockfd=socket(AF_INET,SOCK_STREAM,0);server_address.sin_family=AF_INET;server_address.sin_addr.s_addr=htonl(INADDR_ANY);server_address.sin_port=htons(9734);服务器端改为网络字节序服务器端改为网络字节序例:例:服务器端程序服务器端程序server3.cserver_len=sizeof(server_address);bind(server_sockfd,(struct sockaddr*)&server_address,ser
47、ver_len);listen(server_sockfd,5);while(1)char ch;printf(server waitingn);client_len=sizeof(client_address);client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&client_len);read(client_sockfd,&ch,1);ch+;write(client_sockfd,&ch,1);close(client_sockfd);例:例:服务器端程序服务器端程序server3.c#include
48、#include#include#include#include#include int main()int sockfd;int len;struct sockaddr_in address;int result;char ch=A;sockfd=socket(AF_INET,SOCK_STREAM,0);address.sin_family=AF_INET;客户端客户端 改为网络字节序改为网络字节序例:例:客户端客户端程序程序client3.caddress.sin_addr.s_addr=inet_addr(“127.0.0.1”);/inet_addr的返的返回值已经是网络字节序回值已
49、经是网络字节序address.sin_port=htons(9734);len=sizeof(address);result=connect(sockfd,(struct sockaddr*)&address,len);if(result=-1)perror(oops:client2);exit(1);write(sockfd,&ch,1);read(sockfd,&ch,1);printf(char from server=%cn,ch);close(sockfd);exit(0);例:例:客户端客户端程序程序client3.c通过套接字访问系统提供的标准服务通过套接字访问系统提供的标准服务
50、通过前面介绍的内容,现在可以连接到任何有名字的主机。连接到一个标准服务以替代对字符操作的前述例子UNIX/Linux系统一般提供daytime标准服务,客户可以连接到这个服务来查看服务器的当前日期和时间。首先需要打开首先需要打开daytime服务服务首先安装网络守护进程服务程序xinetd在/etc/xinetd.d路径下,可以看到chargen、daytime、echo等服务daytime服务默认是关闭的,用vi打开daytime,将其中的disable=yes,改为disable=no,保存。然后执行如下命令完成服务开启。$sudo atp-get install xinetd$ls/et