[linux专题]基于linux网络编程

目录

1.网络基础知识

2.建立通信连接

2.1 UDP通信连接过 

2.2 TCP通信过程

2.2.1 TCP状态机

 2.2.2 TCP通信过程

3.通信编码实例

 3.1 UDP编程实例

3.2 TCP编程实例

4. 网络函数介绍

4.1 数据结构

4.2 函数说明

4.2.1 字节顺序函数

4.2.2 字节操作函数

4.2.3 IP地址转换函数

4.2.4 IP地址和域名转换函数

4.2.5 socket函数

4.2.6 bind函数

4.2.7 listen函数

4.2.8 accept函数

4.2.9 connect函数

4.2.10 send函数

4.2.11 recv函数

4.2.12 write 和 read函数

4.2.13 sendto和 recvfrom函数


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状态机

 

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);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值