目录
1.网络基础知识
网络基础知识,可以参考如下博客:
《关于车载以太网理解_AgingMoon的博客-CSDN博客_车载以太网》
《Autosar 以太网 socket 理解_AgingMoon的博客-CSDN博客_autosar 以太网》
《关于车载以太网DHCP的理解_AgingMoon的博客-CSDN博客》
《关于车载以太网 Switch Vlan的理解_AgingMoon的博客-CSDN博客》
《以太网PHY 开发与解析_AgingMoon的博客-CSDN博客》
2.建立通信连接
linux 网络通信编程,主要是围绕socket进行编程,通信的模型一般是C/S模式,以下说明两种常用的通信连接过程。
2.1 UDP通信连接过
2.2 TCP通信过程
tcp的通信过程相对复杂一些,以下结合状态机说明。
2.2.1 TCP状态机
状 态 | 描 述 |
---|---|
CLOSED | 关闭状态,没有连接活动或正在进行 |
LISTEN | 监听状态,服务器正在等待连接进入 |
SYN RCVD | 收到一个连接请求,尚未确认 |
SYN SENT | 已经发出连接请求,等待确认 |
ESTABLISHED | 连接建立,正常数据传输状态 |
FIN WAIT 1 | (主动关闭)已经发送关闭请求,等待确认 |
FIN WAIT 2 | (主动关闭)收到对方关闭确认,等待对方关闭请求 |
TIMED WAIT | 完成双向关闭,等待所有分组死掉 |
CLOSING | 双方同时尝试关闭,等待对方确认 |
CLOSE WAIT | (被动关闭)收到对方关闭请求,已经确认 |
LAST ACK | (被动关闭)等待最后一个关闭确认,并等待所有分组死掉 |
以下说明一般的状态切换过程
2.2.2 TCP通信过程
3.通信编码实例
3.1 UDP编程实例
/*服务端 udp_server.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>
#define PORT (8686)
#define MAX_MSG_SIZE (1024)
int main(void)
{
int sockfd,addrlen,n;
struct sockaddr_in addr;
char msg[MAX_MSG_SIZE];
/*建立UDP socket*/
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if (sockfd < 0)
{
fprintf(stderr,"create socket error: %s \n",strerror(errno));
exit(1);
}
addrlen = sizeof(struct sockaddr_in);
bzero(&addr,addrlen); /*地址清0*/
/*构建socket地址结构*/
addr.sin_family = AF_INET; /*IPv4*/
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
/*绑定端口*/
if (bind(sockfd,(struct sockaddr *)(&addr),addrlen) < 0)
{
fprintf(stderr,"bind socket error: %s \n",strerror(errno));
exit(1);
}
/*等待接收客户端数据*/
while (1)
{
bzero(msg,MAX_MSG_SIZE);
n = recvfrom(sockfd,msg,sizeof(msg),0,(struct sockaddr*)(&addr),&addrlen);
fprintf(stdout,"receive msg is : %s",msg);
/*读取终端输入,并发送给客户端*/
fgets(msg,MAX_MSG_SIZE,stdin);
printf("input msg :%s",msg);
sendto(sockfd,msg,n,0,(struct sockaddr*)(&addr),addrlen);
}
/*关闭通信*/
close(sockfd);
exit(0);
}
/*服务端 udp_client.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#define MAX_MSG_SIZE (1024)
int main(int argc,char **argv)
{
int sockfd,port,addrlen,n;
struct sockaddr_in addr;
char msg[MAX_MSG_SIZE];
/*输入IP 和 端口号*/
if (argc != 3)
{
fprintf(stderr,"usage:%s,server_ip server_port \n",argv[0]);
exit(1);
}
if ((port = atoi(argv[2])) < 0)
{
fprintf(stderr,"usage:%s,server_ip server_port \n",argv[0]);
exit(1);
}
/*建立UDP socket*/
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if (sockfd < 0)
{
fprintf(stderr,"create socket error: %s \n",strerror(errno));
exit(1);
}
addrlen = sizeof(struct sockaddr_in);
bzero(&addr,addrlen); /*地址清0*/
/*构建socket地址结构*/
addr.sin_family = AF_INET; /*IPv4*/
addr.sin_port = htons(port);
if (inet_aton(argv[1],&addr.sin_addr) < 0)
{
fprintf(stderr,"IP error: %s \n",strerror(errno));
exit(1);
}
#if 0 /*no need for client*/
/*绑定端口*/
if (bind(sockfd,(struct sockaddr *)(&addr),addrlen) < 0)
{
fprintf(stderr,"bind socket error: %s \n",strerror(errno));
exit(1);
}
#endif
/*等待发送客户端输入数据*/
while (1)
{
bzero(msg,MAX_MSG_SIZE);
fgets(msg,MAX_MSG_SIZE,stdin); /*读取终端输入,并发送给服务端*/
sendto(sockfd,msg,strlen(msg),0,(struct sockaddr*)(&addr),addrlen);
printf("input msg :%s",msg);
n = recvfrom(sockfd,msg,strlen(msg),0,(struct sockaddr*)(&addr),&addrlen);
fprintf(stdout,"receive msg is : %s",msg);
}
/*关闭通信*/
close(sockfd);
exit(0);
}
3.2 TCP编程实例
/*tcp_server.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>
#include <unistd.h>
#define MAX_MSG_SIZE (1024)
#define LISTEN_SOCK_NUM (5)
int main(int argc,char **argv)
{
int sockfd,new_fd;
struct sockaddr_in srv_addr;
struct sockaddr_in clt_addr;
int sin_size,port
char msg[MAX_MSG_SIZE];
/*输入端口号*/
if (argc != 2)
{
fprintf(stderr,"usage:%s port \n",argv[0]);
exit(1);
}
if ((port = atoi(argv[1])) < 0)
{
fprintf(stderr,"usage:%s port \n",argv[0]);
exit(1);
}
/*创建socket*/
if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
fprintf(stderr,"create socket error:%s \n",strerror(errno));
exit(1);
}
/*sockaddr 地址信息*/
bzero(&srv_addr,sizeof(struct sockaddr_in));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
srv_addr.sin_port = htons(port);
/*bind端口信息*/
if (bind(sockfd,(struct sockaddr *)(&srv_addr),sizeof(struct sockaddr)) == -1)
{
fprintf(stderr,"bind error:%s",strerror(errno));
exit(1);
}
/*监听端口信息*/
if (listen(sockfd,LISTEN_SOCK_NUM) == -1)
{
fprintf(stderr,"listen error:%s",strerror(errno));
exit(1);
}
while (1)
{
sin_size = sizeof(struct sockaddr_in);
/*接收客户端连接*/
if ((new_fd = accept(sockfd,(struct sockaddr*)(&clt_addr),&sin_size)) == -1)
{
fprintf(stderr,"accept error:%s",strerror(errno));
exit(1);
}
printf("connect sucessfully,input msg to send \n");
if (fget(msg,sizeof(msg),stdin) != msg)
{
printf("fget error\n");
exit(1);
}
/*发送消息*/
if (write(new_fd,msg,strlen(msg)) == -1)
{
fprintf(stderr,"write error:%s",strerror(errno));
exit(1);
}
close(new_fd);
}
close(sockfd);
exit(0);
}
/*tcp_client.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>
#include <unistd.h>
#define MAX_MSG_SIZE (1024)
int main(int argc,char **argv)
{
int sockfd;
struct sockaddr_in srv_addr;
struct hostent *host;
int port,n;
char msg[MAX_MSG_SIZE];
/*输入端口号*/
if (argc != 3)
{
fprintf(stderr,"usage:%s hostname port \n",argv[0]);
exit(1);
}
if ((host = gethostbyname(argv[1])) == NULL)
{
fprintf(stderr,"gethostname error \n");
exit(1);
}
if ((port = atoi(argv[2])) < 0)
{
fprintf(stderr,"usage:%s port \n",argv[0]);
exit(1);
}
/*创建socket*/
if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
fprintf(stderr,"create socket error:%s \n",strerror(errno));
exit(1);
}
/*sockaddr 地址信息*/
bzero(&srv_addr,sizeof(struct sockaddr_in));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr = *((struct in_addr*)host->h_addr);
srv_addr.sin_port = htons(port);
/*连接请求*/
if (connect(sockfd,(struct sockaddr *)(&srv_addr),sizeof(struct sockaddr)) == -1)
{
fprintf(stderr,"connect error:%s",strerror(errno));
exit(1);
}
/*读取服务端数据*/
if ((n = read(sockfd,msg,MAX_MSG_SIZE)) == -1)
{
fprintf(stderr,"readerror:%s",strerror(errno));
exit(1);
}
msg[n] = '\0'
printf("received msg:%s \n",msg);
close(sockfd);
exit(0);
}
4. 网络函数介绍
4.1 数据结构
/*套接字数据结构,头文件 <sys/socket.h>*/
struct sockaddr
{
uint8_t sa_len; /*<! 地址长度*/
sa_family_t sa_family; /*<! 地址协议族 AF_xx*/
char sa_data[14]; /*<! 协议地址*/
};
/*IPV4套接字数据结构,头文件 <netinet.h/in.h>*/
struct socketaddr_in
{
uint8_t sin_len; /*<! 地址长度*/
sa_family_t sin_family; /*<! 地址协议族 IPV4:AF_INET*/
in_port_t sin_port; /*<! 端口号,网络字节顺序*/
struct in_addr sin_addr; /*<! 32bit IPV4地址*/
unsigned char sin_zero[8]; /*<! 填充位*/
};
4.2 函数说明
4.2.1 字节顺序函数
因为不同主机的芯片,字节的顺序不一致,包括大端模式和小端模式,也称为主机字节序(host)。
大端模式:高字节存低地址,低字节存高地址,(符合从左到右的阅读方式),小端模式则相反。
那么网络编程中,为了统一,采用的是网络字节序(network) 对应的其实是大端模式。
/* 1. 函数中h表示host, to 表示 to ,n表示network,l表示long ,s表示short
* 2. 包含头文件 <netinet/in.h>
*/
/*32bit 网络字节序转换*/
uint32_t htonl(uint32_t hostlong);
/*16bit 网络字节序转换*/
uint16_t htons(uint16_t hostshort);
/*32bit 主机字节序转换*/
uint32_t htonl(uint32_t netlong);
/*16bit 主机字节序转换*/
uint16_t htons(uint16_t netshort);
4.2.2 字节操作函数
/* 1. b开头表示支持套接口系统提供,mem开头表示是ANSI C提供
* 2. 包含头文件 <string.h>
*/
/*给一块地址清零*/
void bzero(void *dest,size_t nbytes);
/*复制内存函数*/
void bcopy(const void *src,void *dest,size_t nbytes);
/*比较内存数据的大小 ptr1 > ptr2 返回值>0 ptr1==ptr2 返回值=0,否则返回值<0*/
int bcmp(const void *ptr1,const void *ptr2,size_t nbytes);
/*其他三个mem相关函数*/
void memset(void *dest,int c,size_t len);
void memcpy(void *dest,const void *src,size_t nbytes);
int memcmp(const void *ptr1,const void *ptr2,size_t nbytes);
4.2.3 IP地址转换函数
我们用到的IP地址 是 192.168.0.1,套接字中存储是用32bit的网络字节序存储
他们相互转换函数如下:
/* 1. IP地址转换函数,n表示net, a表示字符串的地址
* 2. 包含头文件 <arpa/inet.h>
*/
/* 1. 将字符窜的IP地址,转换为网络字节序地址
* 2. 成功 返回1,失败返回0
*/
int inet_aton(const char *straddr,struct in_addr *addrptr);
/* 1. 将网络字节序地址,转换为字符窜的IP地址
* 2. 成功 返回地址串,失败返回NULL
*/
char* inet_ntoa(struct in_addr inaddr);
/* 1. 将字符窜的IP地址,转换为网络字节序地址
* 2. 成功 返回32bit地址,失败返回INADDR_NONE
*/
in_addr_t inet_addr(const char *straddr);
4.2.4 IP地址和域名转换函数
/* 1. 如典型的 www.baidu.com --> IP地址
* 2. 包含头文件 <netdb.h>
*/
struct hostent
{
char *h_name; /*!< 主机正式的名称*/
char *h_aliases; /*!< 主机的别名*/
int h_addrtype; /*!< 主机的地址类型 如IPv4 AF_INET*/
int h_length; /*!< 主机的地址长度 IPV4 4bytes*/
char **h_addr_list; /*!< 主机的IP地址列表*/
};
#define h_addr h_addr_list[0]; /*主机的第一个IP地址*/
struct hostent *gethostbyname(const char *hostname);
struct hostent *gethostbyaddr(const char *addr,size_t len,int family);
4.2.5 socket函数
#include <sys/types.h>
#include <sys/socket.h>
/*PF 表示protocol family, AF 表示 address family*/
/* @desp: 创建一个套接字描述符
* @param: family,地址家族,PF_UNIX,PF_INET-(IPV4),PF_INET6-(IPV6)等
* @param: type,通信字节流,SOCK_STREAM-(TCP),SOCK_DGRAM-(UDP),SOCK_RAW-(原始) SOCK_PACKET-(链路数据)
* @param: protocol,协议,一般为0
* @return:创建成功则返回 fd
*/
int socket(int family,int type,int protocol);
4.2.6 bind函数
#include <sys/types.h>
#include <sys/socket.h>
/* @desp: 绑定端口和IP地址
* @param: sockfd,socket 创建的fd
* @param: my_addr 表示需要绑定的地址和端口号
* @param: addrlen my_addr 地址长度
* @return: 成功返回0,失败返回-1
*/
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);
4.2.7 listen函数
#include <sys/types.h>
#include <sys/socket.h>
/* @desp: 监听网络的客户机
* @param: sockfd,socket 创建的fd
* @param: backlog 表示最大同时处理连接请求数目
* @return: 成功返回0,失败返回-1
*/
int listen(int sockfd,int backlog);
4.2.8 accept函数
#include <sys/types.h>
#include <sys/socket.h>
/* @desp: 接受客户机的连接请求
* @param: sockfd,socket 创建的fd
* @param: addr,接收的远程主机信息
* @param: addrlen,接收的远程主机信息长度
* @return: 成功返回新的fd,失败返回-1
*/
int accept(int sockfd,struct aockaddr *addr,socketlen_t *addrlen);
4.2.9 connect函数
#include <sys/types.h>
#include <sys/socket.h>
/* @desp: 客户机的连接请求
* @param: sockfd,socket 创建的fd
* @param: addr,连接的服务主机地址
* @param: addrlen,连接的主机地址长度
* @return: 成功返回0,失败返回-1
*/
int connect(int sockfd,const struct aockaddr *serv_addr,int addrlen);
4.2.10 send函数
#include <sys/types.h>
#include <sys/socket.h>
/*flags 标志说明*/
#define MSG_OOD /*out-of-band方式发出 */
#define MSG_DONTROUTE /*取消路由表查询 */
#define MSG_WAITALL /*设置为不可阻断传输 */
#define MSG_NOSIGNAL /*传输不可被SIGPIPE信号中断 */
/* @desp: 发送数据
* @param: sockfd,socket 创建的fd
* @param: msg,待发送的消息
* @param: len,消息长度
* @param: flags,发送标志位,
* @return: 成功返回0,失败返回-1
*/
int send(int sockfd,const void* msg,int len,unsigned int flags);
4.2.11 recv函数
#include <sys/types.h>
#include <sys/socket.h>
/*flags 标志说明*/
#define MSG_OOD /*接收out-of-band方式发出的数据 */
#define MSG_PEEKE /*返回的数据不会再系统内删除 */
#define MSG_WAITALL /*强迫接收到len大小的数据后才能返回 */
#define MSG_NOSIGNAL /*传输不可被SIGPIPE信号中断 */
/* @desp: 接收数据
* @param: sockfd,socket 创建的fd
* @param: buf,接收的消息
* @param: len,消息长度
* @param: flags,发送标志位,
* @return: 成功返回0,失败返回-1
*/
int recv(int sockfd,void* buf,int len,unsigned int flags);
4.2.12 write 和 read函数
#include <unistd.h>
/* @desp: 发送数据
* @param: fd,socket 创建的fd
* @param: buf,发送的消息
* @param: len,消息长度
* @return: 已成功发送的数据,失败返回-1
*/
ssize_t write(int fd,const void *buf,size_t count);
/* @desp: 接收数据
* @param: fd,socket 创建的fd
* @param: buf,接收的消息
* @param: len,消息长度
* @return: 已成功接收的数据,失败返回-1
*/
ssize_t read(int fd,void *buf,size_t count);
4.2.13 sendto和 recvfrom函数
#include <sys/socket.h>
/* @desp: 发送数据
* @param: fd,socket 创建的fd
* @param: buf,发送的消息
* @param: len,消息长度
* @param: flags,通常设置为0
* @return: 已成功发送的数据,失败返回-1
*/
int sendto(int sockfd,const void *msg,int len,unsigned int flags);
/* @desp: 接收数据
* @param: fd,socket 创建的fd
* @param: buf,接收的消息
* @param: len,消息长度
* @param: flags,通常设置为0
* @param: fromaddr,接收的消息地址
* @param: addrlen,接收的消息地址长度
* @return: 已成功接收的数据,失败返回-1
*/
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *fromaddr,int *addrlen);