TCP/UDP 网络编程

字节序

  • 字节序
    • 是指多字节数据的存储顺序

分类:
1、小端格式:将低位字节数据存储在低地址
2、大端格式:将高位字节数据存储在低地址
注意:
LSB:低地址
MSB:高地址

在这里插入图片描述

如何判断当前系统的字节序:

#include <stdio.h>

// 判断当前系统的字节序

union un
{
    int a;
    char b;
};

int main()
{
    union un myun;
    myun.a = 0x12345678;

    printf("a = %#x\n", myun.a);
    printf("b = %#x\n", myun.b);

    if(myun.b == 0x78)
    {
        printf("小端存储\n");
    }
    else
    {
        printf("大端存储\n");
    }

    return 0;
}

在这里插入图片描述

字节序转换函数

字节序转换函数

特点:

1、网络协议指定了通讯字节序是大端
2、只有在多字节数据处理时才需要考虑字节序
3、运行在同一台计算机上的进程相互通信时,一般不用考虑字节序
4、异构计算机之间通讯,需要转换自己的字节序为网络字节序

在需要字节序转换的时候一般调用特定字节序转换函数

#include <arpa/inet.h>

host ==> network

   uint32_t htonl(uint32_t hostlong);

[htonl、htons][主机字节序数据转换为网络字节序数据]

在这里插入图片描述

   uint16_t htons(uint16_t hostshort);

在这里插入图片描述

network ==> host

   uint32_t ntohl(uint32_t netlong);

[ntohl、ntohs][网络字节序数据转换为主机字节序数据]

在这里插入图片描述

   uint16_t ntohs(uint16_t netshort);

在这里插入图片描述

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    int a = 0x12345678;
    short b = 0x1234;

    printf("%#x\n", htonl(a));
    printf("%#x\n", htons(b));

    return 0;
}

在这里插入图片描述

在这里插入图片描述


地址转换函数

  • 地址转换函数

人为识别的ip地址是点分十进制的字符串形式,但是计算机或者网络中识别的ip地址是整型数据,所以需要转化。

inet_pton->字符串ip地址转整型数据

#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);

family 协议族

这里的 family 协议族:
	1、	AF_INET   IPV4网络协议
	2、	AF_INET6  IPV6网络协议

在这里插入图片描述

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    char ip_str[] = "192.168.3.103";
    unsigned int ip_int = 0;
    unsigned char *ip_p = NULL;

    // 将点分十进制ip地址转化为32位无符号整型数据
    inet_pton(AF_INET, ip_str, &ip_int);
    printf("ip_int = %d\n", ip_int);

    ip_p = (char *)&ip_int;
    printf("ip_int = %d,%d,%d,%d\n", *ip_p, *(ip_p+1), *(ip_p+2), *(ip_p+3));

    return 0;
}

在这里插入图片描述

inet_ntop->整型数据转字符串格式ip地址

#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

在这里插入图片描述

在这里插入图片描述

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    unsigned char ip_int[] = {192, 168, 3, 103};
    char ip_str[16] = "";   //    ***.***.***.***\0  16个字节

    // 将点分十进制ip地址转化为32位无符号整型数据
    inet_ntop(AF_INET, &ip_int, ip_str, 16);
    printf("ip_s = %s\n", ip_str);

    return 0;
}

在这里插入图片描述


扩展的:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

   int inet_aton(const char *cp, struct in_addr *inp);

   in_addr_t inet_addr(const char *cp);

   in_addr_t inet_network(const char *cp);

   char *inet_ntoa(struct in_addr in);

   struct in_addr inet_makeaddr(int net, int host);

   in_addr_t inet_lnaof(struct in_addr in);

   in_addr_t inet_netof(struct in_addr in);

inet_addr()和inet_ntoa() 这两个函数只能用在ipv4地址的转换

inet_addr->点分十进制ip转化为整形

在这里插入图片描述

inet_ntoa->整形转化为点分十进制ip

在这里插入图片描述

UDP


UDP


socket


socket

提供不同主机上的进程之间的通信

socket特点

1、socket也称“套接字”
2、是一种文件描述符,代表了一个通信管道的一个端点
3、类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作
4、得到socket套接字(描述符)的方法调用socket()

socket分类

1、SOCK_STREAM,流式套接字,用于TCP
2、SOCK_DGRAM,数据报套接字,用于UDP
3、SOCK_RAW,原始套接字,对于其他层次的协议操作时需要使用这个类型

UDP编程CS架构


UDP编程CS架构

在这里插入图片描述

UDP网络编程流程

服务器:

1、创建套接字socket()
2、将服务器的ip地址、端口号与套接字进行绑定bind()
3、接收数据recvfrom()
4、发送数据sendto()

客户端:

1、创建套接字socket()
2、发送数据sendto()
3、接收数据recvfrom()
4、关闭套接字close()


socket->创建socket套接字

创建socket套接字

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

在这里插入图片描述
在这里插入图片描述
特点:

1、创建套接字时,系统不会分配端口
2、创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的

创建UDP套接字demo

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<stdlib.h>


int main()
{
    // 使用socket函数创建套接字
    // 创建一个用于UDP网络编程的套接字

    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))  == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockfd = %d\n", sockfd);

    return 0;
}

在这里插入图片描述

UDP编程-发送、绑定、接收数据

套接字地址结构[sockaddr_in]

  • IPv4套接字地址结构

在网络编程中经常使用的结构体 sockaddr_in
在这里插入图片描述

char sin_zero[8] 填充,不起什么作用。

通用结构体 sockaddr

通用结构体 sockaddr
在这里插入图片描述
注意:以上3个结构体在Linux系统中已经定义

  • 两种地址结构体使用场合

在定义源地址和目的地址结构的时候,选用struct sockaddr_in;

struct sockaddr_in my_addr;

在调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换

bind(sockfd, (struct sockaddr*) &my_addr, sizeof(my_addr));

发送数据 sendto 函数

  • 发送数据-sendto函数

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, 
			 const void *buf, 
		     size_t len, 
		     int flags,
	   	     const struct sockaddr *dest_addr,
			 socklen_t addrlen);

在这里插入图片描述
在这里插入图片描述

向网络调试助手发送数据

  • 向网络调试助手发送数据
#include <stdio.h>   // printf
#include <sys/types.h>   //
#include <sys/socket.h>  // socket
#include<stdlib.h>   // exit
#include<netinet/in.h>  // sockaddr_in
#include<arpa/inet.h>   // htons  inet_addr
#include<unistd.h>   // close
#include<string.h>

#define N 128

int main(int argc, char *argv[])
{
    // argc为argument count的缩写,代表参数的个数
    // argv为argument vector的缩写,可以理解成参数序列
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s ip port\n", argv[0]);
        exit(1);
    }

    // 第一步:使用socket函数创建套接字,创建一个用于UDP网络编程的套接字

    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))  == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockfd = %d\n", sockfd);

    // 第二步:填充服务器网络信息结构体 sockaddr_in
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET;  // 协议族,AF_INET:ipv4网络协议

    // serveraddr.sin_addr.s_addr = inet_addr("173.0.2.33");  // ip地址
    // serveraddr.sin_port = htons(8080);

    // atoi函数是将字符串转换成整数
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // ip地址
    serveraddr.sin_port = htons(atoi(argv[2]));       // 端口号

    // 第三步:发送数据
    char buf[N] ="";
    while (1)
    {
        fgets(buf, N, stdin);
        buf[strlen(buf) - 1] = '\0';  // 把buf字符串中的\n转化为\0

        if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&serveraddr, addrlen) == -1)
        {
            perror("fail to sendto");
            exit(1);
        }
    }

    // 第四步:关闭套接字文件描述符
    close(sockfd);

    return 0;
}

在这里插入图片描述


绑定bind函数

绑定bind函数

  • 接收端:使用bind函数,来完成地址结构与socket套接字的绑定,这样ip和port就固定了
  • 发送端:在sendto函数中指定接收端的ip、port,就可以发送数据了

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

在这里插入图片描述
在这里插入图片描述

bind示例

#include <stdio.h>   // printf
#include <sys/types.h>   //
#include <sys/socket.h>  // socket
#include<stdlib.h>   // exit
#include<netinet/in.h>  // sockaddr_in
#include<arpa/inet.h>   // htons  inet_addr
#include<unistd.h>   // close
#include<string.h>

// bind示例,一般服务器都需要执行bind函数

int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s ip port\n", argv[0]);
        exit(1);
    }

    // 第一步:使用socket函数创建套接字,创建一个用于UDP网络编程的套接字

    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))  == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    // printf("sockfd = %d\n", sockfd);

    // 第二步:填充服务器网络信息结构体 sockaddr_in
    struct sockaddr_in serveraddr;
    // socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET;  // 协议族,AF_INET:ipv4网络协议

    // atoi函数是将字符串转换成整数
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // ip地址
    serveraddr.sin_port = htons(atoi(argv[2]));       // 端口号

    // 第三步:将网络信息结构体与套接字绑定

    if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
    {
        perror("fail to bind");
        exit(1);
    }

    return 0;
}

在这里插入图片描述
tu1

接收数据recvfrom函数

接收数据recvfrom函数

#include <sys/types.h>
#include <sys/socket.h>

   ssize_t recv(int sockfd, void *buf, size_t len, int flags);

   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);

   ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

在这里插入图片描述
在这里插入图片描述

接收网络调试助手发送的数据

此时网络调试助手作为客户端,写的代码程序作为服务器

#include <stdio.h>   // printf
#include <sys/types.h>   //
#include <sys/socket.h>  // socket
#include<stdlib.h>   // exit
#include<netinet/in.h>  // sockaddr_in
#include<arpa/inet.h>   // htons  inet_addr
#include<unistd.h>   // close
#include<string.h>

#define N 128

int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s ip port\n", argv[0]);
        exit(1);
    }

    // 第一步:使用socket函数创建套接字,创建一个用于UDP网络编程的套接字

    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))  == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    // printf("sockfd = %d\n", sockfd);

    // 第二步:填充服务器网络信息结构体 sockaddr_in
    struct sockaddr_in serveraddr;
    // socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET;  // 协议族,AF_INET:ipv4网络协议

    // atoi函数是将字符串转换成整数
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // ip地址 173.0.205.84
    serveraddr.sin_port = htons(atoi(argv[2]));       // 端口号 9999

    // 第三步:将网络信息结构体与套接字绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
    {
        perror("fail to bind");
        exit(1);
    }

    // 第四步:接收数据
    // char buf[N] = "";
    struct sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(struct sockaddr_in);

    while (1)
    {
        char buf[N] = "";
    if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
    {
        perror("fail to recvfrom");
        exit(1);
    }

    // 打印接收到的数据 打印客户端的ip地址和port
    printf("ip:%s, port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
    printf("from clients: %s\n", buf);
    }

    return 0;
}

在这里插入图片描述


UDP客户端、服务端注意点

在这里插入图片描述

客户端代码

#include <stdio.h>   // printf
#include <sys/types.h>   //
#include <sys/socket.h>  // socket
#include<stdlib.h>   // exit
#include<netinet/in.h>  // sockaddr_in
#include<arpa/inet.h>   // htons  inet_addr
#include<unistd.h>   // close
#include<string.h>


int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s ip port\n", argv[0]);
        exit(1);
    }


    int sockfd;   // 文件描述符
    struct sockaddr_in serveraddr;  // 服务器网络信息结构体
    socklen_t addrlen = sizeof(serveraddr);

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    // 客户端自己指定的ip地址和端口号  这一步一般不需要,系统会自动分配
#if 0
    struct sockaddr_in clientaddr;
    clientaddr.sin_family = AF_INET;  // 协议族,AF_INET:ipv4网络协议
    // atoi函数是将字符串转换成整数
    clientaddr.sin_addr.s_addr = inet_addr(argv[3]);  // 客户端的ip地址
    clientaddr.sin_port = htons(atoi(argv[4]));       // 客户端的端口号

    if(bind(sockfd, (struct sockaddr *)&clientaddr, addrlen) < 0)
    {
        perror("fail to bind");
        exit(1);
    }
#endif

    // 填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;  // 协议族,AF_INET:ipv4网络协议
    // atoi函数是将字符串转换成整数
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // ip地址
    serveraddr.sin_port = htons(atoi(argv[2]));       // 端口号

    char buf[32] = "";
    // 进行通信
    while (1)
    {
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';  // 把buf字符串中的\n转化为\0

        if(sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, addrlen) == -1)
        {
            perror("fail to sendto");
            exit(1);
        }

        char text[32] = "";
        if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&serveraddr, &addrlen) == -1)
        {
            perror("fail to recvfrom");
            exit(1);
        }

        // 打印接收到的数据
        printf("from server: %s\n", text);
    }

    close(sockfd);  // 关闭文件描述符
    return 0;
}

在这里插入图片描述

服务端代码

#include <stdio.h>   // printf
#include <sys/types.h>   //
#include <sys/socket.h>  // socket
#include<stdlib.h>   // exit
#include<netinet/in.h>  // sockaddr_in
#include<arpa/inet.h>   // htons  inet_addr
#include<unistd.h>   // close
#include<string.h>


int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s ip port\n", argv[0]);
        exit(1);
    }


    int sockfd;   // 文件描述符
    struct sockaddr_in serveraddr;  // 服务器网络信息结构体
    socklen_t addrlen = sizeof(serveraddr);

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    // 填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;  // 协议族,AF_INET:ipv4网络协议
    // atoi函数是将字符串转换成整数
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // ip地址
    serveraddr.sin_port = htons(atoi(argv[2]));       // 端口号

    // 将网络信息结构体与套接字绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
    {
        perror("fail to bind");
        exit(1);
    }

    // 进行通信
    while (1)
    {
        char text[32] = "";
        struct sockaddr_in clientaddr;
        if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
        {
            perror("fail to recvfrom");
            exit(1);
        }
        printf("[%s - %d]\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

        strcat(text, " *_*");   // strcat(函数) 将两个char类型连接

        if(sendto(sockfd, text, sizeof(text), 0, (struct sockaddr *)&clientaddr, addrlen) == -1)
        {
            perror("fail to sendto");
            exit(1);
        }
    }

    close(sockfd);  // 关闭文件描述符
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值