Linux 学习记录30(网络编程篇)

Linux 学习记录30(网络编程篇)

在这里插入图片描述

本文目录

  • Linux 学习记录30(网络编程篇)
  • 一、跨主机传输
    • 1. 本地字节序与网络字节序
    • 2. 字节序转换函数
      • (1. htons htonl 主机字节序-->网络字节序
      • (2. ntohs ntohl 网络字节序---->主机字节序
    • 3. 结构体对齐
      • (1. 修改默认对齐系数
      • (2. 取消结构体对齐
    • 4. 类型长度
  • 二、IP地址转换
    • 1.点分十进制->网络字节序
      • (1. inet_aton
      • (2. inet_pton
      • (3. inet_addr 最常用
    • 2.网络字节序->点分十进制
      • (1. inet_ntoa 常用
      • (2. inet_ntop
  • 三、套接字 socket
    • 1. 套接字概念
    • 2. socket函数
  • 四、搭建TCP服务器
    • 1. 定义宏函数打印报错信息
    • 2. 创建流式套接字
    • 3. 绑定套接字
      • (1. bind函数
      • (2. 绑定服务器的IP和端口
    • 4. 将套接字设定为被动监听状态
      • (1. listen函数
      • (2. 设置
    • 5. 获取链接成功后的套接字
      • (1. accept函数
    • 6. 通过文件描述符接收来自客户端的数据
      • (1. recv函数
      • (2. 使用
    • 7. 关闭文件
    • 8. 允许端口被快速重用
    • 9. 服务器发送数据
  • 五、搭建TCP客户端
    • 1. 创建流式套接字
    • 2. 连接服务器
      • (1. connect
  • 六、搭建UDP服务器
    • 1. 创建报式套接字
    • 2. 绑定服务器IP和端口
    • 3. 接收
      • (1. recvfrom函数
    • 4. 允许端口被快速重用
    • 5. 发送
      • (1. sendto函数
  • 思维导图
  • 程序汇总
    • 1. TCP服务器搭建
    • 2. TCP客户端搭建
    • 3. UDP服务器搭建
    • 4. UDP客户端搭建

一、跨主机传输

1. 本地字节序与网络字节序

1. 本地字节数:主机字节序(Host Byte Order) HBO
2. 网络字节序(Network Byte Order) NBO,网络字节序规定使用大端宇节序
3. 在跨主机传输过程中,需要使用统一的字节序,即网络字节序,避免兼容性问题

2. 字节序转换函数

主机字节序和网络字节序的转换

(1. htons htonl 主机字节序–>网络字节序

头文件:
       #include uint32_t htonl(uint32_t hostlong);
       uint16_t htons(uint16_t hostshort);
参数:
    指定要转换成网络字节序的整型:分别是32bit和16bit;
返回值:
    成功,返回转换后网络字节序的整型
    
#include 
#include int main(int argc, const char *argv[])
{
    unsigned int a = 0x87654321;
    printf("%#x\n", a);             //0x87654321
    printf("%#x\n", htonl(a));      //0x21436587printf("%#x\n", htons(a));      //0x2143                 
    
    return 0;
}   

(2. ntohs ntohl 网络字节序---->主机字节序

头文件:
       #include 
原型:
       uint32_t ntohl(uint32_t netlong);
       uint16_t ntohs(uint16_t netshort);
参数:
    uint32_t hostlong:32位网络字节序整型;
    uint16_t hostshort:16位网络字节序整型;
​
返回值:
    成功,返回转换成主机字节序的整型;

3. 结构体对齐

编译器会对结构体进行对齐,加速CPU取值周期,由于数据对齐也是与操作系统相关,不同的主机如果使用不同的对齐方式,会导致数据无法解析。
所以网络传输结构体的时候需要取消结构体对齐;

(1. 修改默认对齐系数

#pragma pack(1) 设置默认对齐系数 :() 中的参数只能填2^n (n=0,1,2,3,4,5......)

(2. 取消结构体对齐

__attribute__((packed)) :取消该结构体对齐
====================================
typedef struct
{
	int a;
	float b;
	char c
}__attribute__((packed)) buff_type;

4. 类型长度

因为涉及到跨平台,不同平台会有不同的字长

intlongint不同操作系统这两个数据类型所占的字节数可能是不一样的
解决方式:可以通过通用类型: uint8_t,uint16_t,uint32_t

如:

typedef struct
{
	uint8_t a;
	uint16_t b;
	uint32_t c;
}buff_type;

二、IP地址转换

由于IP地址本质上是一个4个字节的整数,所以在跨主机传输中也有字节序的概念

所以需要将IP地址转换成网络字节序
1. "192.168.8.189”---->本机字节序的整型0xCOA808BD---->网络字节序0xBD08A8C0
2. "192.168.31.42"---->0xC0A81F2A---->0x2A1FA8C0
3. "192168.2145"----本机字节序的整型0xCOA80291--->网络字节序的整型数0x9102A8C0

1.点分十进制->网络字节序

(1. inet_aton

头文件:
       #include 
       #include 
       #include 
原型:
       int inet_aton(const char *cp, struct in_addr *inp);
参数:
    char *cp:源IP地址的点分十进制字符串,例如 “192.168.1.10;
    struct in_addr *inp:存储转换成网络字节序的IP;
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
返回值:
    成功,返回非0;
    失败,返回0;
​
只能转换IPv4
    
例子:
 #define IP  "192.168.1.10"      //0xC0A8010A
 int main(int argc, const char *argv[])
 {
     struct in_addr inp;
 
     if(inet_aton(IP, &inp) == 0)
     {
         printf("转换失败\n");
         return -1;
     }
 
     printf("%#X\n", inp.s_addr);    //0X0A01A8C0
                                                           
     return 0;
 }                     

(2. inet_pton

头文件:
       #include 
原型:
       int inet_pton(int af, const char *src, void *dst);
参数:
    int af:协议族
            AF_INET         IPV4
            AF_INET6        IPV6
     char *src:指定要转换成网络字节序的点分十进制字符串;
     void* dst
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
​
            af == AF_INETa;
            struct in6_addr
            {
            }
返回值:
    成功,返回1;
    失败,返回0或者-1,更新errno;#define IP "192.168.1.3" //0xC0A80103 --> 0x301A8C0 struct in_addr inp;
    inet_pton(AF_INET, IP, &inp);
    printf("%#X\n", inp.s_addr);        //0x301A8C0 

(3. inet_addr 最常用

头文件:
       #include 
       #include 
       #include 
原型:
       uint32_t inet_addr(const char *cp);
参数:
     char *cp:源IP地址的点分十进制字符串,例如 “192.168.1.10;
返回值:
    成功,返回转换后的网络字节序IP地址;
                    typedef uint32_t in_addr_t;
​
    失败,返回INADDR_NONE (usually -1);
​
只能转换IPv4;
​
例子:
    printf("%#X\n", inet_addr(IP));

2.网络字节序->点分十进制

(1. inet_ntoa 常用

头文件:
       #include 
       #include 
       #include 
原型:
       char *inet_ntoa(struct in_addr in);
参数:
    struct in_addr in:指定要转换成点分十进制字符串的IP地址;
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
返回值:
    成功,返回点分十进制字符串的首地址;
​
只能转换IPv4;printf("%s\n", inet_ntoa(inp));

(2. inet_ntop

头文件:
       #include 
原型:
       const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数:
    int af:协议族
            AF_INET         IPV4
            AF_INET6        IPV6
    void* src:存储要转换成点分十进制字符串的IP首地址;
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
​
            af == AF_INETa;
            struct in6_addr
            {
            }
    char *dst:存储转换后的结果,点分十进制的首地址;
    socklen_t size:缓冲区大小,其实就是指定多大的空间用于转换IP;
返回值:
    成功,返回字符串的首地址,就是dst;
    失败,返回NULL,更新errno;     
​
例子:
     char ip[20];
     if(inet_ntop(AF_INET, &inp, ip, sizeof(ip)) == NULL)
     {
         perror("ient_ntop");
         return -1;                                            
     }
     printf("%s\n", ip);

三、套接字 socket

1. 套接字概念

  1. 最早的套接字和共享内存,消息队列,管道一样,只能直线一个主机内部的进程间通信。
  2. 后期加入了TCP/IP协议,是的套接字能够支持不同主机之间的进程间通信。
  3. socket函数,可以在内核空间中创建两块缓冲区,供于发送数据,接收数据。

2. socket函数

功能:在内核空间中创建一个接收缓冲区,一个发送缓冲区,并获取到套接字文件描述符;
原型:
       #include           /* See NOTES */
       #include 

       int socket(int domain, int type, int protocol);
参数:
    int domain:协议族,地址族:
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   Local communication              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7)
       
     int type:     
        SOCK_STREAM:字节流式套接字,流式套接字。默认指定的是TCP协议
        SOCK_DGRAM:数据报式套接字,报式套接字。默认指定的是UDP协议。
        SOCK_RAW:原始套接字,协议需要在第三个参数指定。
        
    int protocol:默认协议填0;
                IPPROTO_TCP   IPPROTO_UDP
返回值:
    成功,返回套接字文件描述符;
    失败,返回-1,更新errno;

四、搭建TCP服务器

Linux 学习记录30(网络编程篇)_第1张图片

1. 定义宏函数打印报错信息

/*输入打印的错误类型 打印错误信息,行,具体函数*/
#define ERR_MSG(msg) do{\
    fprintf(stderr, "Line:%d :%s %s\n",__LINE__, __FILE__,__func__);\
    perror(msg);\
}while(0)

2. 创建流式套接字

int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{//如果创建失败则
    ERR_MSG("socket");
}

3. 绑定套接字

(1. bind函数

函数原型:int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:将地址信息结构体与套接字我加你绑定
参数1:套接字文件描述符,就是socket的返回值
参数2:通用地址信息结构体,真实的地址信息结构体给v就地址族指定
	需要手动填充绑定到套接字上的IP和端口号
参数3:真实地址信息的大小
返回值:成功返回0,失败返回-1,并置位错误码
===========================================================
地址信息结构体:
struct sockaddr_in {
sa_family_t    sin_family; /* address family: AF_INET */必须填AF_INET
in_port_t      sin_port;   /* port in network byte order */端口号的网络字节序,1024~49151
struct in_addr sin_addr;   /* internet address */本机IP地址的网络字节序
};

/* Internet address. */
struct in_addr {
uint32_t       s_addr;     /* address in network byte order */
};

(2. 绑定服务器的IP和端口

struct sockaddr_in sin_addr;//定义通用地址信息结构体
char TCP_ADDR[20] = {0};//存放本机IP
sin_addr.sin_family = AF_INET;//必须填AF_INET
sin_addr.sin_port = htons(8080);//端口号
printf("input address : ");
fgets(TCP_ADDR,sizeof(TCP_ADDR),stdin);
sin_addr.sin_addr.s_addr = inet_addr(TCP_ADDR);//填充IP地址
/*绑定服务器的IP和端口*/
if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
{
    ERR_MSG("socket");
}

4. 将套接字设定为被动监听状态

当套接字设置为被动监听状态,则会让内核维护两个队列,未完成连接的队列,已完成连接的队列

(1. listen函数

函数原型:int listen(int sockfd, int backlog);
功能:将套接字设定为被动监听状态
参数1:指定的套接字
参数2:允许同时多少个客户端建立连接;就是未完成连接的队列的容量
返回值:成功返回0,失败返回-1,并置位错误码

(2. 设置

/*将套接字设定为被动监听状态*/
if(listen(sfd,BACKLOG) == -1)
{
    ERR_MSG("listen");
}

5. 获取链接成功后的套接字

(1. accept函数

函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:阻塞等待客户端函数,若客户端连接成功,则会从已完成连接的队列头在获取一个客户端信息
	生成一个新的文件描述符,新文件描述符才是与客户端通信的文件描述符
参数1:转换成被动监听状态的文件描述符,提供该描述符才能找到已完成的队列
参数2:通用地址信息结构体,真实的地址信息结构体,根据地址族指定
	函数运行后会存储连接成功的客户端的地址信息,若不想获取填NULL
参数3:真实的地址信息结构体的大小,是一个指针类型
返回值:成功回新的文件描述符,失败返回-1并置位错误码

6. 通过文件描述符接收来自客户端的数据

(1. recv函数

函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:通过套接字文件描述符接收数据
参数1:通信套接字
参数2:读取任意类型的数据
参数3:指定读取的字节数
参数4:读取方式
	0:阻塞方式,当填0,则该函数完全等价于read
	MSG_DONTWAIT:不阻塞,如果没有数据则函数运行失败
	MSG_CMSG_CLOEXEC:
	MSG_ERRQUEUE:
	MSG_OOB:
	MSG_PEEK:
	MSG_TRUNC:
	MSG_WAITALL:
返回值:成功返回接收到的字节数>0,失败返回-1并置位错误码
	在流式套接字:当对端关闭返回0

(2. 使用

/*接收来自客户端的数据*/
    char str[128] = {0};
    int res = 0;
    printf("接收客户端数据\r\n");
    while (1)
    {
        bzero(str,sizeof(str));//清空字符串
        res = recv(newfd,str,sizeof(str),0);//阻塞接收字符串
        if(res == -1)
        {
            perror("recv");
        }else if(res == 0)
        {
            printf("客户端关闭\r\n");
            return -1;
        }else
        {
            printf("%s\r\n",str);
        }
    }

7. 关闭文件

close(sfd);//关闭套接字文件
close(newfd);//关闭通讯

8. 允许端口被快速重用

int reuse = 1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
    {
        ERR_MSG("setsockopt");
        return -1;
    }

9. 服务器发送数据

函数原型:ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数1:通信套接字文件描述符
参数2:指定要发送的数据的首地址
参数3:指定要发送的字节数
参数4:发送方式
	0:阻塞方式,当填0,则该函数完全等价于read
	MSG_DONTWAIT:不阻塞,如果没有数据则函数运行失败

五、搭建TCP客户端

1. 创建流式套接字

int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{//如果创建失败则
    ERR_MSG("socket");
    return -1;
}

2. 连接服务器

(1. connect

函数原型:int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:连接服务器
参数1:
参数2:
参数3

六、搭建UDP服务器

Linux 学习记录30(网络编程篇)_第2张图片

1. 创建报式套接字

/*创建报式套接字*/
    int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("创建报式套接字...\r\n");
    if(cfd < 0)
    {//如果创建失败则
        ERR_MSG("socket");
        return -1;
    }
    printf("创建成功\r\n");

2. 绑定服务器IP和端口

/*绑定绑定服务器IP和端口到文件描述付*/
    struct sockaddr_in sin_addr;
    char server_IP[20] = {0};//存储服务器IP
    int server_port = 0;//存储服务器端口
    sin_addr.sin_family = AF_INET;
    printf("input server IP : \r\n");
    fgets(server_IP, sizeof(server_IP),stdin);//获取服务器IP
    printf("input server port : \r\n");
    scanf("%d",&server_port);//获取服务器端口
    printf("绑定服务器IP和端口......\r\n");
    if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("绑定成功\r\n");

3. 接收

(1. recvfrom函数

函数原型:ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数1:通信套接字;
参数2:存储读取到的数据,可以读取任意类型数据
参数3:指定要读取多少个字节
参数4:读取方式
	0:阻塞方式,当没有数据的时候,该函数阻塞
	MSG_DONTWAIT:非阻塞方式,当没有数据的时候,该函数不阻塞,立即返回,且函数运行失败;
参数5:存储该数据包从谁哪里发过来的,存储发送方的地址信息。若不想接收可以填NULL
参数6:真实的地址信息结构体大小,注意是指针类型,所以需要在外面定一个普通变量
	获取大小若地址信息结构体填NULL,则该参数填NULL:
返回值:成功返回接收到的字节数
	0:对端关闭。仅限于TCP
	-1:失败,并更新错误码;

4. 允许端口被快速重用

/*允许端口被快速重用*/
	int reuse = 1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
    {
        ERR_MSG("setsockopt");
        return -1;
    }

5. 发送

(1. sendto函数

函数原型:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
功能:发送数据到指定的地址
参数1:通信的套接字文件描述符
参数2:指定要发送的数据的首地址
参数3:指定要发送的字节数
参数4:接收端的地址信息结构体
参数5:
返回值:

思维导图

Linux 学习记录30(网络编程篇)_第3张图片

程序汇总

1. TCP服务器搭建

int main(int argc, char const *argv[])
{
    time_t sys_t = time(NULL);
    struct tm *fomattime = localtime(&sys_t);
    char time_str[50] = {0};

    /*创建流式套接字*/
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    printf("创建流式套接字...\r\n");
    if(sfd < 0)
    {//如果创建失败则
        ERR_MSG("socket");
        return -1;
    }
    printf("创建成功\r\n");

    /*允许端口被快速重用*/
    int reuse = 1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
    {
        ERR_MSG("setsockopt");
        return -1;
    }

    /*绑定服务器的IP和端口*/
    struct sockaddr_in sin_addr;//定义本机通用地址信息结构体
    char TCP_ADDR[20] = {0};//存放本机IP
    int port = 0;
    sin_addr.sin_family = AF_INET;//必须填AF_INET
    printf("input address : ");
    fgets(TCP_ADDR,sizeof(TCP_ADDR),stdin);//获取IP
    printf("input port : ");
    scanf("%d",&port);//获取端口号
    sin_addr.sin_port = htons(port);//端口号
    sin_addr.sin_addr.s_addr = inet_addr(TCP_ADDR);//填充IP地址
    printf("绑定服务器IP和端口......\r\n");
    if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("绑定成功\r\n");

    /*将套接字设定为被动监听状态*/
    printf("设置监听模式\r\n");
    if(listen(sfd,BACKLOG) == -1)
    {
        ERR_MSG("listen");
        return -1;
    }
    printf("设置完成\r\n");

    /*获取链接成功后的与客户端通信的套接字文件描述符s*/
    struct sockaddr_in addr;//存储客户端的地址信息
    socklen_t addr_len;
    int newfd;
    addr_len = sizeof(addr);
    while(1)
    {
        printf("等待客户端链接......\r\n");
        if((newfd = accept(sfd,(struct sockaddr*)&addr,&addr_len)) == -1)
        {
            ERR_MSG("accept");
            return -1;
        }
        printf("客户端已链接\r\n");
        printf("客户端IP : %s\r\n",inet_ntoa(addr.sin_addr));
        printf("客户端端口 : %d\r\n",ntohs(addr.sin_port));
    }
    /*接收来自客户端的数据*/
    char str[128] = {0};
    int res = 0;
    printf("接收客户端数据\r\n");
    
    bzero(str,sizeof(str));//清空字符串
    res = recv(newfd,str,sizeof(str),0);//阻塞接收字符串
    if(res == -1)
    {
        perror("recv");
    }else if(res == 0)
    {
        printf("客户端关闭\r\n");
        return -1;
    }else
    {
        GET_TIME(time_str);//获取时间戳
        printf("%s : %s\r\n",time_str,str);
        TCP_printf(newfd,"%s\r\n",str);
    }

    close(sfd);//关闭套接字文件
    close(newfd);//关闭通讯

    return 0;
}

2. TCP客户端搭建

int main(int argc, char const *argv[])
{
    time_t sys_t = time(NULL);
    struct tm *fomattime = localtime(&sys_t);
    char time_str[50] = {0};

    /*创建流式套接字*/
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    printf("创建流式套接字...\r\n");
    if(cfd < 0)
    {//如果创建失败则
        ERR_MSG("socket");
        return -1;
    }
    printf("创建成功\r\n");

    /*连接服务器*/
    struct sockaddr_in cin_addr;//定义本机通用地址信息结构体
    cin_addr.sin_family = AF_INET;//必须填AF_INET
    char TCP_ADDR[20] = {0};//存放服务器IP
    int TCP_PORT = 0;
    printf("input address : ");
    fgets(TCP_ADDR,sizeof(TCP_ADDR),stdin);//获取IP
    printf("input port : ");
    scanf("%d",&TCP_PORT);//获取端口号
    cin_addr.sin_port = htons(TCP_PORT);//端口号
    cin_addr.sin_addr.s_addr = inet_addr(TCP_ADDR);//填充IP地址
    printf("连接服务器......\r\n");
    if(connect(cfd,(const struct sockaddr*)&cin_addr,sizeof(cin_addr)) == -1)//链接服务器
    {
        ERR_MSG("connect");
        return -1;
    }
    printf("服务器连接完成\r\n");

    /*接收来自服务器的数据*/
    char str[128] = {0};
    char buf[128] = {0};
    int flage = 0;
    int res = 0;
    printf("接收服务器数据\r\n");
    while (1)
    {
        bzero(str,sizeof(str));//清空字符串
        fgets(str,sizeof(str),stdin);
        // buff(str,sizeof(str?)
        flage = send(cfd,str,sizeof(str),0);
        if(flage == -1)
        {
            perror("send");
            return 0;
        }
        
        res = recv(cfd,buf,sizeof(buf),0);
        if(res == -1)
        {
            perror("recv");
            return 0;
        }else if(res == 0)
        {
            printf("服务器关闭\r\n");
        }else
        {
            printf("");
        }
    }

    close(cfd);//关闭套接字文件
    return 0;
}

3. UDP服务器搭建

int main(int argc, char const *argv[])
{
    time_t sys_t = time(NULL);
    struct tm *fomattime = localtime(&sys_t);
    char time_str[50] = {0};

    /*创建报式套接字*/
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("创建报式套接字...\r\n");
    if(sfd < 0)
    {//如果创建失败则
        ERR_MSG("socket");
        return -1;
    }
    printf("创建成功\r\n");

    /*允许端口被快速重用*/
    int reuse = 1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
    {
        ERR_MSG("setsockopt");
        return -1;
    }

    /*绑定绑定服务器IP和端口到文件描述付*/
    struct sockaddr_in sin_addr;
    char server_IP[20] = {0};//存储服务器IP
    int server_port = 0;//存储服务器端口
    sin_addr.sin_family = AF_INET;
    printf("input server IP : ");
    fgets(server_IP, sizeof(server_IP),stdin);//获取服务器IP
    printf("input server port : ");
    scanf("%d",&server_port);//获取服务器端口
    sin_addr.sin_addr.s_addr = inet_addr(server_IP);
    sin_addr.sin_port = htons(server_port);
    printf("绑定服务器IP和端口......\r\n");
    if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("绑定成功\r\n");

    /*接收消息*/
    struct sockaddr_in cin_addr;
    socklen_t cin_len = sizeof(cin_addr);
    char str[128];
    int res = 0;
    printf("开始接收数据\r\n");
    while(1)
    {
        bzero(str, sizeof(str));
        if(recvfrom(sfd,str,sizeof(str),0,(struct sockaddr*)&cin_addr,&cin_len) == -1)
        {
            ERR_MSG("recvfrom");
            return -1;
        }
        printf("UDP get : %s\r\n",str);//终端打印接收到的数据
        sendto(sfd,str,sizeof(str),0,(struct sockaddr*)&cin_addr,cin_len);//按原地址发回
    }
    close(sfd);
    
    return 0;
}

4. UDP客户端搭建

int main(int argc, char const *argv[])
{
    time_t sys_t = time(NULL);
    struct tm *fomattime = localtime(&sys_t);
    char time_str[50] = {0};

    /*创建报式套接字*/
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("创建报式套接字...\r\n");
    if(sfd < 0)
    {//如果创建失败则
        ERR_MSG("socket");
        return -1;
    }
    printf("创建成功\r\n");

    /*允许端口被快速重用*/
    int reuse = 1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
    {
        ERR_MSG("setsockopt");
        return -1;
    }

    /*绑定绑定服务器IP和端口到文件描述付*/
    struct sockaddr_in cin_addr;
    char server_IP[20] = {0};//存储服务器IP
    int server_port = 0;//存储服务器端口
    cin_addr.sin_family = AF_INET;
    printf("input server IP : ");
    fgets(server_IP, sizeof(server_IP),stdin);//获取服务器IP
    printf("input server port : ");
    scanf("%d",&server_port);//获取服务器端口
    cin_addr.sin_addr.s_addr = inet_addr(server_IP);
    cin_addr.sin_port = htons(server_port);

    /*接收消息*/
    socklen_t cin_len = sizeof(cin_addr);
    char str[128];
    char buf[128];
    int res = 0;
    printf("开始发送数据\r\n");
    while(1)
    {
        bzero(buf, sizeof(buf));
        fgets(str, sizeof(str),stdin);

        if(sendto(sfd,str,sizeof(str),0,(struct sockaddr*)&cin_addr,cin_len) == -1)
        {
            ERR_MSG("sendto");
            return -1;
        }

        if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin_addr,&cin_len) == -1)
        {
            ERR_MSG("recvfrom");
            return -1;
        }
        printf("UDP get : %s\r\n",buf);
    }
    close(sfd);
    
    return 0;
}

你可能感兴趣的:(Linux学习记录,网络,linux,学习)