嵌入式Linux网络编程
参考书籍:《从实践中学嵌入式linux应用程序开发》(华清远见嵌入式学院)
代码下载:http://download.csdn.net/detail/klcf0220/5354885
资料下载:http://www.kuaipan.cn/file/id_43409466388906157.htm(http://download.csdn.net/detail/klcf0220/7058371) 、 http://www.kuaipan.cn/file/id_43409466388906143.htm
参考链接:http://www.cnblogs.com/Robotke1/archive/2013/05/07/3064575.html
TCP/IP协议
参考链接: http://www.cnblogs.com/dann/archive/2013/02/14/2909869.html
OSI层次模型:
OSI层次模型中各层的功能:
(1) 物理层(PH),确定物理设备接口,提供点-点的比特流传输的物理链路;
(2) 数据链路层(DL),利用差错处理技术,提供高可靠传输的数据链路;
(3) 网络层(N),利用路由技术,实现用户数据的端-端传输;
(4) 运输层(T),屏蔽子网差异,用户要求和网络服务之间的差异;
(5) 会话层(S),提供控制会话和数据传输的手段 ;
(6) 表示层(P),解决异种系统之间的信息表示问题,屏蔽不同系统在数据表示方面的差异;
(7) 应用层(A),利用下层的服务,满足具体的应用要求。
TCP/IP协议各层的解析:
(1)网络接口层
TCP/IP协议模型的基层,负责数据帧的发送和接收。对应OSI模型中的物理层和数据链路层,是TCP/IP的最底层,不过通常在描述TCP/IP模型时还是会划分具体为物理层(PHY)和数据链路层(MAC)。主要负责将二进制流转换为数据帧,并进行数据帧的发送和接收。数据帧是网络传输的基本单元。
(2)网络层
通过互联协议将数据包封装成互联网数据包,并运行必要的路由算法。这里有4种互联协议。
(a)网际协议IP:负责在主机和网络之间的路径寻址和数据包路由。
(b)地址解析协议ARP:获得同一物理网络中的主机硬件地址。
(c)网际控制消息协议ICMP:发送消息,并报告有关数据包的传送错误。
(d)互联组管理协议IGMP:用来实现本地多路广播路由器报告。
(3)传输层
传输协议负责提供应用层程序之间的通信服务。传输协议的选择根据数据传输方式而定。主要有以下2种传输协议:
(a)传输控制协议TCP:为应用程序提供可靠的通信连接,适用于要求得到响应的应用程序。
(b)用户数据包协议UDP:提供无连接通信,且不对传输包进行可靠性确认。
(4)应用层
应用程序通过这一层访问网络,主要包括常见的FTP、HTTP、DNS和TELNET协议。
套接字
Socket有三种类型:
1)流式套接字(SOCK_STREAM) 基于TCP协议
2)数据报套接字(SOCK_DGRAM) 基于UDP协议
3)原始套接字(SOCK_RAW) 基于底层协议如IP或ICMP协议,主要用于新的网络协议的开发。
在socket程序设计中,struct sockaddr 和struct sockaddr_in用于记录网络地址,
struct scokaddr { unsigned short sa_family;//地址族 char sa_data[14];//14字节的协议地址,包含该socket的IP地址和端口号 }; struct scokaddr_in { short int sin_family;//地址族 unsigned short int sin_port;//端口号 struct in_addr sin_addr;//IP地址 unsigned char sin_zero[8];//填充0以保持与struct sockaddr同样大小 };
这两个数据类型是等效的,可以相互转化,通常sockaddr_in 数据类型使用更为方便。
sa_family 字段可选的常见值:(需要#include<netinet/in.h>头文件)
AF_INET:IPv4协议
AF_INET6:IPv6协议
AF_LOCAL:UNIX域协议
AF_LINK:链路地址协议
AF_KEY:秘钥套接字
数据存储优先顺序:
字节序的转因为计算机存储有两种字节优先排序:高位字节优先(即大端模式)和低位地址优先(即小端模式),Internet上数据以高位字节优先顺序在网络上传输。所以有些情况下,需要对这两字节存储优先顺序进行相互转化。这里用到4个函数:htons() ,htonl() ,ntohs() ,ntohl();
其中h代表host,n代表network,s代表short,l代表long。通常16位的IP端口号用 s 代表,而IP地址用 l 来表示。
地址格式转换:
用户表达地址时通常采用点分十进制表示的数值字符串,而在通常使用的socket编程中所用的则是二进制值,这就需要将两个数值进行转换。
在IPv4中用到的函数有inet_aton(),inet_addr(),inet_ntoa();
而IPv4和IPv6兼容的函数有inet_pton()和inet_ntop()。
套接字编程
socket() //创建一个socket
bind() //该函数用于将sockaddr结构的地址信息与套接字进行绑定。主要用于TCP的连接,而在UDP的连接中则没有必要。
connect() //该函数用于服务器建立连接
listen() //创建一个等待队列,在其中存放未处理的客户端连接请求。
accept() //用于等待并接收来自客户端的socket连接请求
send() ,sendto //发送数据
recv() ,recvfrom //接收数据
Socket网络编程流程图:http://blog.csdn.net/zhangyaowen123123/article/details/6738562
编程实例:
/*server.c*/ #include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<errno.h> #include<string.h> #include<unistd.h> #include<netinet/in.h> #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; int sockfd,client_fd; char buf[BUFFER_SIZE]; //建立socket链接 if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) { perror("socket"); exit(1); } printf("socket is = %d\n",sockfd); //设置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)); //绑定套接字 if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } printf("bind success\n"); //listen() if(listen(sockfd,MAX_QUE_CONN_NM) == -1) { perror("listen"); exit(1); } printf("listen ......\n"); //accept() sin_size = sizeof(client_sockaddr); if((client_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size)) == -1) { perror("accept"); exit(1); } //recv() memset(buf,0,sizeof(buf)); if((recvbytes = recv(client_fd,buf,BUFFER_SIZE,0)) == -1) { perror("recv"); exit(1); } printf("received a message:%s\n",buf); close(sockfd); exit(0); }
/*client.c*/ #include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<errno.h> #include<string.h> #include<unistd.h> #include<netinet/in.h> #include<netdb.h>//一定要加上这个头文件,不然会报“提领指向不完全类型的指针”的错误 #define PORT 4321 #define BUFFER_SIZE 1024 int main(int argc,char *argv[]) { struct sockaddr_in serv_addr; int sendbytes,sockfd; struct hostent *host; char buf[BUFFER_SIZE]; if(argc < 3) { fprintf(stderr,"USAGE: ./client Hostname(or ip address) Text\n"); exit(1); } //地址解析函数 if((host = gethostbyname(argv[1])) == NULL) { perror("gethostbyname"); exit(1); } memset(buf,0,sizeof(buf)); sprintf(buf,"%s",argv[2]); //建立socket if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) { perror("socket"); exit(1); } //设置sockaddr_in结构体中相关参数 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero),8); //connect() if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) == -1) { perror("connect\n"); exit(1); } //send() if((sendbytes = send(sockfd,buf,strlen(buf),0)) == -1) { perror("send"); exit(1); } close(sockfd); exit(0); }