TCP/IP网络协议通信函数接口

news2024/11/29 1:55:03

创建套接字函数 socket

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
int socket(int domain, int type, int protocol);
【函数功能】
        socket 函数创建一个通信端点,并返回一个引用该端点的文件描述符,称为套接字。
【参数含义】
        [domain]: 地址族(Address Family),也就是 IP 地址类型
        [type]:创建的套接字类型,主要有 SOCK_STREAM(流式套接字),SOCK_DGRAM(数据包套接字)等
        [protocol]:指定具体的传输协议类型。通常某协议中只有一种特定类型,这样 protocol参数仅能设置为 0;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。
【返回值】
        成功返回一个套接字描述符(>0,与文件描述符类型)
        失败返回-1.errno 被设置
【备注】
        domain 参数可选字段:

以下是 socket.h 文件中的宏,可以看到 AF_和 PF_都是可以的,所以在之后的代码中如果看到
上面表格中不存在的字段时,不要认为写错了哟。
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
#define AF_ROSE PF_ROSE
#define AF_DECnet PF_DECnet
#define AF_NETBEUI PF_NETBEUI
#define AF_SECURITY PF_SECURITY
#define AF_KEY PF_KEY
#define AF_NETLINK PF_NETLINK
#define AF_ROUTE PF_ROUTE
#define AF_PACKET PF_PACKET
#define AF_ASH PF_ASH
#define AF_ECONET PF_ECONET
#define AF_ATMSVC PF_ATMSVC
#define AF_RDS PF_RDS
#define AF_SNA PF_SNA
#define AF_IRDA PF_IRDA
#define AF_PPPOX PF_PPPOX
#define AF_WANPIPE PF_WANPIPE
#define AF_LLC PF_LLC
#define AF_IB PF_IB
#define AF_MPLS PF_MPLS
#define AF_CAN PF_CAN
#define AF_TIPC PF_TIPC
#define AF_BLUETOOTH PF_BLUETOOTH
#define AF_IUCV PF_IUCV
#define AF_RXRPC PF_RXRPC
#define AF_ISDN PF_ISDN
#define AF_PHONET PF_PHONET
#define AF_IEEE802154 PF_IEEE802154
#define AF_CAIF PF_CAIF
#define AF_ALG PF_ALG
#define AF_NFC PF_NFC
#define AF_VSOCK PF_VSOCK
#define AF_KCM PF_KCM
#define AF_QIPCRTR PF_QIPCRTR
#define AF_SMC PF_SMC
#define AF_MAX PF_MAX

type 可选字段:

error 字段:

【示例程序】
创建一个用于 TCP 通信的套接字:
int sockfd = socket ( AF_INET , SOCK_STREAM , 0 );
创建一个用于 UDP 通信的套接字:
int sockfd = socket ( AF_INET , SOCK_DGRAM , 0 );
创建一个用于本地通信的套接字:
int sockfd = socket ( PF_UNIX , SOCK_STREAM , 0 );

网络地址结构体 sockaddr

        因为套接字接口既可以用于以太网,也可以用于其他类型的通信,如蓝牙等等,不同的通信协 议,它们的地址结构是不一样的,所以设计了一个通用地址结构体: 类型为 struct sockaddr
/* 描述通用套接字地址的结构 */
struct sockaddr
{
    __SOCKADDR_COMMON (sa_); /* 常用数据:地址族和长度. */
    char sa_data[14]; /* 地址数据. */
};

        socket 中所有的函数都是使用上面的结构来表示一个地址(可以任意协议族的地址)

IPV4 的地址信息结构体为:

struct sockaddr_in {
    __kernel_sa_family_t sin_family; /*指定的协议族,AF_INTE*/
    __be16 sin_port; /* 端口号(网络字节序)*/
    struct in_addr sin_addr; /*IP 地址*/
    /*填充到结构 sockaddr 的大小,保证所有协议的地址结构体都可以使用 socket 表示. */
    unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
    sizeof(unsigned short int) - sizeof(struct in_addr)];
};

其中:

struct in_addr {
    __be32 s_addr; //IP 地址,in_addr_t 类型,32 位无符号整型
};

使用上面结构体需要的头文件为:

#include <arpa/inet.h>
#include <netinet/in.h> // 为了使用 IPV4 地址结构体

本地通信的地址信息结构体为:

struct sockaddr_un {
    __kernel_sa_family_t sun_family; /* 协议族,AF_UNIX */
    char sun_path[UNIX_PATH_MAX]; /* 路径名,因为本地通信就是进程间通信,没有 IP 地址一言,所以需要一个计算机中的目录路径,就像 ftok 通过路径名获取 key 一样 */
};

使用上面结构体需要的头文件为:

#include <arpa/inet.h>
#include <netinet/un.h> // 为了使用 IPV4 地址结构体

点分式 IP 地址转 in_addr

点分式 ip 转换成 in_addr 类型(inet_aton)

【头文件】
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
【函数原型】
int inet_aton(const char *cp, struct in_addr *inp);
【函数功能】
        将点分式 ip 地址转换成 in_addr 类型
【参数含义】
        [cp]: IP 地址的点分式字符串
        [inp]:保存转换好的 in_addr 结构体类型
【返回值】
        成功返回 0,
        失败返回-1,同时 errno 被设置
【示例】
struct sockaddr_in saddr ;
inet_aton ( "192.168.41.243" , & saddr . sin_addr );

点分式 ip 转换 in_addr_t 类型(inet_addr)

【头文件】
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
【函数原型】
in_addr_t inet_addr(const char *cp);
【函数功能】
        将点分式 ip 地址转换成 in_addr_t 类型 , 转换后是网络字节序
【参数含义】
        [cp]: IP 地址的点分式字符串
【返回值】
        成功返回转换好的 in_addr_t 类型的 ip 地址
        失败返回-1
【备注】
        当 IP 为 255.255.255.255 时,函数会认为这是个无效 IP,这属于 linux 的遗留问题
【示例】
struct sockaddr_in Saddr; //保存服务器的地址信息的结构体
memset(&Saddr,0,sizeof(struct sockaddr_in)); //清空结构体
Saddr.sin_family = AF_INET; //协议族,AF_INET 表示使用 IPV4 的协议族
Saddr.sin_port = htons(atoi(argv[2])); //端口号,但是要求的是网络字节序
Saddr.sin_addr.s_addr = inet_addr(argv[1]); //32bit 的一个 IP 地址

点分式 ip 转换 in_addr_t 类型(inet_network)

【头文件】
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
【函数原型】
in_addr_t inet_network(const char *cp);
【函数功能】
        将点分式 ip 地址转换成 in_addr_t 类型,转换后是主机字节序
【参数含义】
        [cp]: IP 地址的点分式字符串
【返回值】
        成功返回转换好的 in_addr_t 类型的 ip 地址
        失败返回-1
【备注】
        当 IP 为 255.255.255.255 时,函数会认为这是个无效 IP,这属于 linux 的遗留问题

适用与 IPV6 协议的 ip 转换(inet_pton)

【头文件】
#include <arpa/inet.h>
【函数原型】
int inet_pton(int af, const char *src, void *dst);
【函数功能】
        将字符串类型的 IP 地址转换为二进制类型。
【参数含义】
        [af]: 表示网络类型的协议族,如在 IPv4 下的值为 AF_INET;
        [src]: 存放要转换的字符串
        [dst]:存放转换后的结果,在 IPv4 下,dst 指向结构 struct in_addr 的指针。
【返回值】
        当函数 inet_pton()的返回值为-1 的时候,通常是用于 af 所指定的协议族不支持造成,此时 errno 的返回值为 EAFNOSUPPORT;当函数的返回值为 0 时,表示 src 指向的值不是合法的 IP地址;当函数的返回值为正值时,表示转换成功。

网络 IP 转点分式

in_addr 结构体类型 ip 转点分式(inet_ntoa)

【头文件】
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
【函数原型】
        char * inet_ntoa ( struct in_addr in);
【函数功能】
        将 in_addr 类型 ip 转换成点分式表示
【参数含义】
        [in]: 要转换的 in_addr 类型 IP
【返回值】
        成功返回转换好的点分式 ip 地址
        失败返回 NULL
【示例】
        打印客户端的 IP 地址
printf ( "[ %s ] \n " , inet_ntoa ( addr . sin_addr ));

适用与 IPV6 协议的 ip 转换(inet_pton)

【头文件】
#include <arpa/inet.h>
【函数原型】
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
【函数功能】
        将二进制的网络 IP 地址转换为字符串。
【参数含义】
         [af]: 表示网络类型的协议族,如在 IPv4 下的值为 AF_INET;
        [src]:为需要转换的二进制 ip 地址,在 IPv4 下,dst 指向结构 struct in_addr 的指针。
        [dst]:存放转换后的结果
        [size]:为 dst 的空间大小
【返回值】
        返回一个指向 dst 的指针。当发生错误时,返回 NULL。当 af 设定的协议族不支持时,errno 设置为 EAFNOSUPPORT;当 dst 缓冲区大小过小的时候 errno 的值为 ENOSPC

inet_makeaddr()函数,inet_lnaof()函数和 inet_netof()函数

inet_makeaddr 函数

【头文件】
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
【函数原型】
struct in_addr inet_makeaddr(int net,int host)
【函数功能】
        一个主机的 IP 地址分为网络地址和主机地址,inet_makeaddr()函数将主机字节序的网络地址 net 和主机地址 host 合并成一个网络字节序的 IP 地址。
【参数含义】
        [net]: 存放网络号参数(二进制形式的网络字节序)
        [host]: 存放主机号地址(二进制形式的主机字节序)
【返回值】
        返回一个网络字节序的 IP 地址
【示例】
unsigned long net,host;
net = 0x0000007F;
host = 0x00000001;
struct in_addr ip = inet_makeaddr(net,host);

主机地址提取函数 inet_lnaof

【头文件】
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
【函数原型】
 in_addr_t inet_lnaof(struct in_addr in)
【函数功能】
        从参数 in 中提取出主机地址,执行成功后返回主机字节顺序形式的主机地址。 例如:172.17.242.131 属于 B 类地址,则主机号为低 16 位,主机地址为 0.0.242.131,按主机字节顺序输出则为 0xf283。
【参数含义】
        [in]: 存放的是主机字节序的二进制形式的 IP 地址
【返回值】
        返回主机字节序的二进制形式的 IP 主机部分的数值
【示例】
const char * addr = "127.0.0.1" ;
unsigned long ip = inet_network ( addr );
unsigned long host_id = inet_lnaof ( ip );

网络地址提取函数 inet_netof

【头文件】
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
【函数原型】
in_addr_t inet_netof(struct in_addr in)
【函数功能】
        该函数从参数 in 中提取出网络地址,执行成功后返回主机字节顺序形式的网络地址。 如:172.17.242.131 属于 B 类地址,则高 16 位表示网络号,网络地址为 172.17.0.0。
【参数含义】
        [in]: 存放的是主机字节序的二进制形式的 IP 地址
【返回值】
        返回主机字节序的二进制形式的 IP 网络部分的数值
【示例】
const char * addr = "127.0.0.1" ;
unsigned long ip = inet_network ( addr );
unsigned long network_id = inet_netof ( ip );

主机字节序和网络字节序之间的转换 API

在 linux 中提供了下面四个函数用于网络字节序和主机字节序之间的转换。

主机转网络: htonl, htons
网络转主机: ntohl, ntohs
其中函数名字的含义如下:
h: host 主机字节序(就是我们所说到的端口号)
n: network 网络字节序
l: long --->32 位
s: short --->16 位

32 位主机字节序转网络字节序(htonl)

【头文件】
#include <arpa/inet.h>
【函数原型】
uint32_t htonl(uint32_t hostlong);
【函数功能】
        将 32 位的主机字节序 hostlong 转换为网络字节序返回
【参数含义】
        [hostlong]: 待转换的 32 位主机字节序
【返回值】
        返回转换后的网络字节序

16 位主机字节序转网络字节序(htons)

【头文件】
#include <arpa/inet.h>
【函数原型】
uint16_t htons(uint16_t hostlong);
【函数功能】
        将 16 位的主机字节序 hostlong 转换为网络字节序返回
【参数含义】
        [hostlong]: 待转换的 16 位主机字节序
【返回值】
        返回转换后的网络字节序
【示例】
        端口号的转换:
address . sin_port = htons ( atoi (port)); // 将端口号 port 转换为网络字节序

32 位网络字节序转主机字节序(ntohl)

【头文件】
#include <arpa/inet.h>
【函数原型】
uint32_t ntohl(uint32_t netlong);
【函数功能】
        将 32 位的网络字节序 netlong 转换为主机字节序返回
【参数含义】
        [netlong]: 待转换的 32 位网络字节序
【返回值】
        返回转换后的主机字节序

16 位网络字节序转主机字节序(ntohs)

【头文件】
#include <arpa/inet.h>
【函数原型】
uint16_t ntohs(uint16_t netlong);
【函数功能】
        将 16 位的网络字节序 netlong 转换为主机字节序返回
【参数含义】
        [netlong]: 待转换的 16 位网络字节序
【返回值】
返回转换后的主机字节序
【示例】
        打印连接的客户端的 IP 和端口号:
printf ( " 客户端 IP: %s , 客户端 Port: %d \n " , inet_ntoa ( caddr . sin_addr ), ntohs ( caddr . sin_port ));

服务器绑定(bind)

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
【函数功能】
        bind 就是将一个本地协议地址赋予一个套接字,对于网络协议,协议地址就是 32 位的 IPV4 地址或 128 位的 IPV6 地址与 16 位的 TCP 或 UDP 端口号的组合。
【参数含义】
        [sockfd]: 待赋予的套接字
        [addr]:保存了网络协议地址的结构体指针,IPV4 为 sockaddr_in。
        [addrlen]:协议地址结构体的大小
【返回值】
        成功返回 0,
        失败返回-1,同时 errno 被设置
【示例】
//2.绑定一个通信 IP 地址(作为服务器本身的 IP)
struct sockaddr_in saddr; //保存服务器的地址(IP+port)
memset(&saddr,0,sizeof(struct sockaddr_in)); //清空结构体
saddr.sin_family = AF_INET;
inet_aton(argv[1], &saddr.sin_addr);
saddr.sin_port = htons(atoi(argv[2]));
int ret = bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret == -1)
{
    perror("bind error");
    exit(-1);
}

服务器监听函数(listen)

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
int listen(int sockfd, int backlog);
【函数功能】
        通过 socket 函数创建的套接字时,它是被假设成一个主动套接字的,也就是说它是一个调用 connect 去连接服务器的客户端套接字,而 listen 函数就是将一个未连接的套接字变成一个被动套接字,指使内核应该接收指向该套接字的连接请求,根据 TCP 三次握手的示例图得知,调用 listen 函数会导致套接字从 CLOSED 状态变为 LISTEN 状态,同时 listen 函数的第二个参数还将指定与该套接字连接的最大个数。
【参数含义】
         [sockfd]: 待监听的套接字
        [backlog]: 监听队列上面最大的请求数量
【返回值】
        成功返回 0,
        失败返回-1,同时 errno 被设置
【示例】
        //3.开启对一个套接字的监听
listen ( sockfd , 250 );

等待客户连接函数(accept)

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
【函数功能】
        accept 函数用于从已完成连接队列队头返回下一个已完成的连接,如果队列为空,则程序睡眠(假定套接字为默认的阻塞模式)
【参数含义】
        [sockfd]: 服务器套接字
        [addr]:用来保存待连接的客户端协议地址结构体
        [addrlen]:用来保存待连接的 客户端协议地址结构体大小
【返回值】
        成功返回一个"连接套接字",表示与一个特定的客户端的连接,后序与这个客户端的通信,都是通过这个连接套接字
        失败返回-1,同时 error 被设置
【示例】
struct sockaddr_in caddr ; // 保存客户端的地址 (IP+port)
socklen_t len = sizeof ( caddr );
int confd = accept (sockfd,( struct sockaddr * ) & caddr , & len );

请求服务器连接函数(connect)

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
【函数功能】
        connect 函数用于客户端和服务器建立连接
【参数含义】
        [sockfd]: 客户端套接字
        [addr]:保存待连接的服务器协议地址结构体
        [addrlen]:保存待连接的 服务器协议地址结构体大小
【返回值】
        成功返回 0
        失败返回-1,同时 errno 被设置
【示例】
struct sockaddr_in Saddr; //保存服务器的地址信息的结构体
memset(&Saddr,0,sizeof(struct sockaddr_in)); //清空结构体
Saddr.sin_family = AF_INET; //协议族,AF_INET 表示使用 IPV4 的协议族
Saddr.sin_port = htons(atoi(3456));//端口号,但是要求的是网络字节序
Saddr.sin_addr.s_addr = inet_addr("192.168.41.234"); 
int r = connect(sock,(struct sockaddr *)&Saddr,sizeof(Saddr));
if(r == -1) //连接失败
{
    perror("connect error");
    return -1;
}

网络消息发送 API

使用 write 发送

        write 函数操作套接字发送消息类似于操作文件描述符向文件中写入数据,write 的函数原型如下:
ssize_t write ( int fd, const void * buf, size_t count);
        在使用 write 进行套接字网络通信时,通常将套接字写入第一个参数,发送的内容写入第二个参数,第三个参数是发送的大小,发送的大小是由窗口大小决定的,一般每次最多发送1024 字节就行了。
使用示例:
char buf[1024] = {0};
memset(buf,0,sizeof(buf));
scanf("%[^\n]",buf);
printf("%ld\n",strlen(buf));
int ret = write(sock,buf,strlen(buf));
if(ret <= 0)
{
    perror("sendto error");
    return -1;
}

使用 writev 发送

【头文件】
#include <sys/uio.h>
【函数原型】
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
【函数功能】
        和 write 函数类似,不过该函数的第二个参数是一个 struct iovec 的数组,该结构体中有两个元素,一个表示一个地址,一个表示地址偏移量,第三个参数表示 struct iovec 数组的元素个数。
【参数含义】
        [fd]: 描述符(文件描述符或套接字描述符)
        [iov]:struct iovec 结构体数组,存放待发送的消息
struct iovec
{
        void * iov_base ; // 内容的地址
        __kernel_size_t iov_len ; // 偏移量
};
        [iovcnt]: struct iovec 结构体数组 的元素个数
【返回值】
        成功返回发送的字节数
        失败返回-1,同时 errno 被设置
【示例】
struct iovec iv [ 2 ];
iv [ 0 ].iov_base = header_buf;
iv [ 0 ].iov_len = strlen ( header_buf );
iv [ 1 ].iov_base = file_buf;
iv [ 1 ].iov_len = file_stat.st_size;
ret = writev (confd, iv , 2 );

使用 send 发送

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
【函数功能】
        在套接字通信中和 write 函数类似,不过多了第四个参数,是一个发送标志,一般为 0
【参数含义】
        [sockfd]: 通信套接字
        [buf]:待发送的内容
        [len]: 发送内容的字节大小
        [flags]: 发送标志,一般为 0,也可以是几个宏的组合值
【返回值】
        成功返回发送的字节数
        失败返回-1,同时 errno 被设置
【示例】
ret = send ( sockfd , "nishiliangzaiya" , 15 , 0 );
【flags 标志选项】
        MSG_CONFIRM :告诉链路层发生了向前的进展:你从另一方获得了成功的回复。如果链路层没 有这样做,它将定期重新探测邻居(例如,通过单播 ARP)。仅在 SOCK_DGRAM 和 SOCK_RAW 套接 字上有效,目前仅对 IPv4 和 IPv6 实现。
        MSG_DONTROUTE:不要使用网关来发送数据,只发送到直接连接的主机上。通常只有诊断或者路由程序会使用,这只针对路由的协议族定义的,数据包的套接字没有。
        MSG_DONTWAIT :启用非阻塞操作,如果操作阻塞,就返回 EAGAIN 或 EWOULDBLOCK
        MSG_EOR :当支持 SOCK_SEQPACKET 时,终止记录。
        MSG_MORE :调用方有更多的数据要发送。这个标志与 TCP 或者 udp 套接字一起使用
        MSG_NOSIGNAL :当另一端中断连接时,请求不向流定向套接字上的错误发送 SIGPIPE ,EPIPE 错误仍然返回。
        MSG_OOB:在支持此概念的套接字上发送带外数据(例如,SOCK_STREAM 类型);底层协议还必须支持带外数据

使用 sendto 发送

【头文件】
#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);
【函数功能】
        在套接字通信中和 send 函数类似,不过多了两个参数用来指定接收者的协议地址和协议地 址大小,因为 UDP 是没有结果连接的,所以一般使用该函数进行通信
【参数含义】
        [sockfd]: 通信套接字
        [buf]:待发送的内容
        [len]: 发送内容的字节大小
        [flags]: 发送标志,一般为 0,也可以是几个宏的组合值
        [dest_addr]:接收者的协议地址
        [addrlen]:接收者协议地址的大小
【返回值】
        成功返回发送的字节数
        失败返回-1,同时 errno 被设置
【示例】
        TCP 通信使用示例:直接将后面两个参数置空处理
ret = sendto (sockfd, "nishiliangzaima" , 15 , 0 , NULL , 0 );
if (ret == - 1 )
{
        perror ( "sendto error" );
}
UDP 通信使用示例
char buf [ 1024 ] = { 0 };
fgets ( buf , 1024 , stdin );
int ret = sendto ( sockfd , buf , strlen ( buf ), 0 ,( struct sockaddr * ) & addr , sizeof ( addr ));

使用 sendmsg 发送

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
【函数功能】
        在套接字通信中和 sendto 函数类似,不过 sendmsg 函数将发送的内容和接收者的协议地址
都放入了一个 struct msghdr 的结构体中。
【参数含义】
        [sockfd]: 通信套接字
        [msg]:保存了发送内容和协议地址等信息的结构体指针
struct msghdr
{
    void *msg_name; /* 发送/接收方的协议地址 */
    socklen_t msg_namelen; /* 地址数据长度 */
    struct iovec *msg_iov; /* 要发送/接收的数据向量*/
    size_t msg_iovlen; /*向量中的元素数。*/
    void *msg_control; /*辅助数据(例如 BSD 文件传递)*/
    size_t msg_controllen; /*辅助数据缓冲区长度。*/
    int msg_flags; /* 接收到的消息上的标志。忽略 */
};
        [flags]: 发送标志,一般为 0,也可以是几个宏的组合值
【返回值】
        成功返回发送的字节数
        失败返回-1,同时 errno 被设置

读取网络消息 API

使用 read 读取

        read 函数操作套接字发送消息类似于操作文件描述符向文件中读取数据,read 的函数原型
如下:
ssize_t read ( int fd, void * buf, size_t count);
        在使用 read 进行套接字网络通信时,通常将套接字写入第一个参数,保存读取的内容的数组写入第二个参数,第三个参数是每次读取的大小。
使用示例:
char buf [ 1024 ];
memset ( buf , 0 , sizeof ( buf ));
int ret = read ( client , buf , sizeof ( buf ));
使用该方式的注意事项:
         使用 read 进行套接字通信时,如果网络两端通信的流量很大的话就会出现数据丢失的现象,例如服务器向客户端传输大文件时,如果使用 read 去进行读取的话,可能会使得读取得文件丢失。这个现象的原因在于内核中用于套接字的缓冲区可能已达到了极限。此时所需要的是调用者再次调用 read 函数,以输出剩余的字节。

使用 readv 读取

【头文件】
#include <sys/uio.h>
【函数原型】
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
【函数功能】
        和 read 函数类似,不过该函数的第二个参数是一个 struct iovec 的数组,该结构体中有两个元素,一个表示一个地址,一个表示地址偏移量,第三个参数表示 struct iovec 数组的元素个数。
【参数含义】
        [fd]: 描述符(文件描述符或套接字描述符)
        [iov]:struct iovec 结构体数组,存放待读取的消息
struct iovec
{
        void * iov_base ; // 内容的地址
        __kernel_size_t iov_len ; // 偏移量
};
        [iovcnt]: struct iovec 结构体数组 的元素个数
【返回值】
        成功返回读取的字节数
        失败返回-1,同时 errno 被设置

使用 recv 读取

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
【函数功能】
        在套接字通信中和 read 函数类似,不过多了第四个参数,是一个发送标志,如果没有其他特别用途,一般指定为 0.
【参数含义】
        [sockfd]: 通信套接字
        [buf]:保存读取的内容
        [len]: 读取内容的字节大小
        [flags]: 读取标志,没有特别用途一般为 0,也可以是几个宏的组合值
【返回值】
        成功返回实际读取的字节数
        失败返回-1,同时 errno 被设置
【flags 读取标志选项】
        [MSG_OOB] 接收以 out-of-band 送出的数据.
        [MSG_PEEK] 返回来的数据并不会在系统内删除, 如果再调用 recv()会返回相同的数据内容.
        [ MSG_WAITALL] 强迫接收到 len 大小的数据后才能返回, 除非有错误或信号产生, 一般大流程 数据通信得时候需要使用 recv 函数指定该标志
        [MSG_NOSIGNAL] 此操作不愿被 SIGPIPE 信号中断,返回值成功则返回接收到的字符数, 失败返回-1,错误原因存于 errno 中

使用 recvfrom 读取

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
【函数功能】
        在套接字通信中和 recv 函数类似,不过多了两个参数用来保存发送者的协议地址和协议地址大小,因为 UDP 是没有结果连接的,所以一般使用该函数进行通信
【参数含义】
        [sockfd]: 通信套接字
        [buf]:保存读取的内容
        [len]: 读取内容的字节大小
        [flags]: 发送标志,一般为 0,也可以是几个宏的组合值
        [dest_addr]:保存发送者的协议地址
        [addrlen]:发送者协议地址的大小
【返回值】
         成功返回实际读取的字节数
        失败返回-1,同时 errno 被设置
【备注】
        TCP 协议不支持这个函数
【示例】
// 读取服务器的回复消息 -UDP 读取
memset (buf, 0 , 1024 );
struct sockaddr_in src_addr ; // 保存消息发送方的地址 (IP+port)
memset ( & src_addr, 0 , sizeof ( struct sockaddr_in)); // 清空结构体
socklen_t src_len = sizeof ( src_addr ); // 保存可用大小
recvfrom (sockfd,buf, 1024 , 0 ,( struct sockaddr * ) & src_addr , & src_len );

使用 recvmsg 读取

【头文件】
#include <sys/types.h>
#include <sys/socket.h>
【函数原型】
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
【函数功能】
        在套接字通信中和 recvfrom 函数类似,不过 recvmsg 函数将发送的内容和接收者的协议地 址都放入了一个 struct msghdr 的结构体中。
【参数含义】
        [sockfd]: 通信套接字
        [msg]:保存了用于存储读取内容的空间地址和发送者协议地址等信息的结构体指针
struct msghdr
{
        void * msg_name ; /* 发送 / 接收方的协议地址 */
        socklen_t msg_namelen ; /* 地址数据长度 */
        struct iovec * msg_iov ; /* 要发送 / 接收的数据向量 */
        size_t msg_iovlen ; /* 向量中的元素数。 */
        void * msg_control ; /* 辅助数据 ( 例如 BSD 文件传递 )*/
        size_t msg_controllen ; /* 辅助数据缓冲区长度。 */
        int msg_flags ; /* 接收到的消息上的标志。忽略 */
};

        [flags]: 发送标志,一般为 0,也可以是几个宏的组合值

【返回值】
         成功返回实际读取的字节数
        失败返回-1,同时 errno 被设置

连接断开函数 shutdown

【头文件】
#include <sys/socket.h>
【函数原型】
int shutdown(int sockfd, int how);
【函数功能】
        该函数可以选择关闭全双工连接的读通道或者写通道,如果两个通道同时关闭,则这个连接不能再继续通信。该只会关闭连接,但是不会释放占用的文件描述符。
【参数含义】
        [sockfd]: 待操作的套接字
        [how]:操作选项,可以选择以下三种:
                SHUT_RD 关闭读
                SHUT_WR 关闭写
                SHUT_RDWR 关闭读写
【返回值】
        成功返回 0
        失败返回-1,同时 errno 被设置
【备注】
        close()函数会同时关闭全双工连接的读写通道,除了关闭连接外,还会释放套接字占用的文件描述符。而 shutdown()只会关闭连接,但是不会释放占用的文件描述符。所以即使使用了 SHUT_RDWR 类型调用 shutdown()关闭连接,也仍然要调用 close()来释放连接占用的文件描述 符。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1069717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++入门-day03

引言&#xff1a;本节我们讲一下C中的引用、内联函数、Auto、范围for 一、引用 先看一下下面这段代码&#xff1a; 在这段代码中。我们命名了两个变量&#xff0c;a和_a&#xff0c;其中_a就是a的引用 所谓引用就是a的“别名”&#xff0c;我们看一下这段代码的运行结果&…

互联网云厂商大转向:在海外重燃新「战事」

2023&#xff0c;云厂出海的第七个年头&#xff0c;三朵云的海外布局都在加速&#xff0c;在“主动出海”的大背景下&#xff0c;云厂的海外战场也正在发生新的变化。 作者|思杭 编辑|皮爷 出品|产业家 中国云厂&#xff0c;正在将目光从东南亚转移至中东。 东南亚的互…

代码随想录算法训练营第四十六天 | 518. 零钱兑换 II、377. 组合总和 Ⅳ

518. 零钱兑换 II 视频讲解&#xff1a;动态规划之完全背包&#xff0c;装满背包有多少种方法&#xff1f;组合与排列有讲究&#xff01;| LeetCode&#xff1a;518.零钱兑换II_哔哩哔哩_bilibili 代码随想录 &#xff08;1&#xff09;代码 377. 组合总和 Ⅳ 视频讲解&…

【哈士奇赠书活动 - 41期】- 〖产品设计软技能:创业公司篇〗

文章目录 ⭐️ 赠书 - 《产品设计软技能&#xff1a;创业公司篇》⭐️ 内容简介⭐️ 作者简介⭐️ 编辑推荐⭐️ 赠书活动 → 获奖名单 ⭐️ 赠书 - 《产品设计软技能&#xff1a;创业公司篇》 ⭐️ 内容简介 在创业公司设计产品与在成熟公司设计产品存在明显差异。《产品设计软…

华为防火墙项目

二、知识点 1&#xff0c;会话表&#xff1a;防火墙通过首包建立会话表&#xff0c;其他非首包通过匹配会话表进行通信&#xff0c;就不用查看安全策略啦。 2&#xff0c;长连接 防火墙为各种协议设定了会话老化机制。当一条会话在老化时间内没有被任何报文匹配&#xff0c;则…

【算法|动态规划No.15】leetcode1035. 不相交的线

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【教程】在RK3568上部署(C++)语义分割算法BiSeNetv1/v2

引言 在本篇教程中&#xff0c;博主将记录国庆假期前在RK3568上部署分割算法的步骤以及代码。首先说一下&#xff0c;RK3568这个开发板本身的算力大概是0.8T&#xff08;在实际开发中还会用到额外的计算卡&#xff0c;额外的计算卡后面文章再说&#xff0c;本篇文章主要记录在…

AQS的简单说明

1.概述 AQS全称AbstractQueuedSynchronizer&#xff0c;是用来实现锁或者队列同步器的公共基础部分的抽象实现&#xff0c;是整个JUC体系的基石&#xff0c;用于解决锁分配给谁的问题&#xff0c;ReentrantLock底层的实现就是AQS。 2.AQS实现原理 AQS内部有一个由volatile修…

正点原子嵌入式linux驱动开发——Linux内核顶层Makefile详解

之前的几篇学习笔记重点讲解了如何移植uboot到STM32MP157开发板上&#xff0c;从本章就开始学习如何移植Linux内核。 同uboot一样&#xff0c;在具体移植之前&#xff0c;先来学习一下Linux内核的顶层Makefile文件&#xff0c;因为顶层 Makefile控制着Linux内核的编译流程。 L…

如何在Apache和Resin环境中实现HTTP到HTTPS的自动跳转:一次全面的探讨与实践

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

提取歌曲伴奏?用对软件一键帮你搞定~

相信大家经常想获取某首歌曲的伴奏&#xff0c;但是不知从何下手&#xff0c;今天这篇教程给大家分享一个超神奇软件&#xff0c;一键提取歌曲伴奏&#xff01; 第一步&#xff1a;打开【音分轨】APP&#xff0c;进入首页点击【人声分离】 第二步&#xff1a;选择导入方式&…

多电脑之间无线访问文件夹传输文件之“电子神偷”

目录 应用场景说明网络共享文件功能开启步骤1&#xff1a;确保电脑开启网络共享功能步骤2&#xff1a;在自己电脑某个盘创建一个文件夹&#xff0c;作为共享文件夹步骤3&#xff1a;查看当前电脑的用户名和ip地址 访问网络共享文件夹&#xff0c;在电脑B访问获取电脑A的文件数据…

Windows版MySql8.0安装(亲测成功!)

下载 下载地址&#xff1a;点我下载 下载完成后将其解压到自定义目录下,我所有的软件都保存在C:\zhushanglin\WindowsSoft&#xff0c;解压完成后会看见以下目录: 配置环境变量 此电脑 右键,然后点属性&#xff0c;步骤如下: 新建MYSQL_HOME系统变量 编辑Path系统变量&a…

读论文:Real-Time Encrypted Traffic Classification via Lightweight Neural Networks

基于轻量级神经网络的实时加密流量分类 0、摘要 提出一种轻量级模型&#xff0c;设计原则“maximize the reuse of thin modules”&#xff0c;thin modules采用多头注意和一维卷积网络。由于所有数据包的一步交互和多头注意力机制的并行计算&#xff0c;所提出的模型的优势是…

RF元素定位

元素定位方式&#xff1a;id, name, link, partial_link_text, xpath, css id 【登录输入框】id session_email_or_mobile_number input text id session_email_or_mobile_numbername 【登录输入框】name session[email_or_mobile_number] input text name sessi…

react-antd 文件导入按钮增加一个加载状态

1、效果图实例: 2、部分代码 2.1 props : 2.2 handleChange、上传的文件检验 : construction中定义 construction(props) { super(props); this.state { loadingStaus: flase, loadingDisabled: flase, // 作用:按钮如果在加 载状态中&#xff0c;没…

Android多线程学习:线程

一、概念 进程&#xff1a;系统资源分配的基本单位&#xff0c;进程之间相互独立&#xff0c;不能直接访问其他进程的地址空间。 线程&#xff1a;CPU调度的基本单位&#xff0c;线程之间共享所在进程的资源&#xff0c;包括共享内存&#xff0c;公有数据&#xff0c;全局变量…

【Pod】

Pod 一、Pod基本概念二、Pod的使用方式pause容器&#xff08;pod的基础容器&#xff09;核心功能pause容器使得Pod中所有容器可以共享两种资源&#xff1a;网络和存储网络存储 三、Pod分类自主式Pod/静态pod控制器管理的Pod 四、三种容器五、镜像拉取策略&#xff08;image Pul…

云计算安全和云原生安全的关系

云计算安全(Cloud Computing Security)指的是在云环境中保护数据、应用程序和基础设施的安全性。它包括保护云服务提供商的基础设施和平台&#xff0c;以及云服务用户的数据和应用程序。 云原生安全(Cloud-Native Security)则是指在云原生环境中保护应用程序和服务的安全性。云…

谁说手机没有高质量抓拍?华为Mate 60系列与Mate X5让你体验“时间凝固”!

我们日常拍照时&#xff0c;经常会出现“照片糊了”的现象&#xff0c;这是由于被拍摄的人或者物快速移动导致。 来源网图&#xff0c;侵删 抓拍&#xff0c;Snap photography&#xff0c;“抓住时机&#xff0c;把瞬间出现的情景拍摄下来拍照”&#xff0c;又名写实抓拍&…