socket 常用操作相关函数介绍(C语言)

本文介绍了网络编程中的基础数据结构,如sockaddr_in结构,以及网络字节序和本地字节序的转换函数。详细阐述了如何使用htons、htonl、ntohs、ntohl进行字节序转换。同时,讲解了主机名与IP地址的转换函数,如inet_addr和inet_ntoa。此外,还涵盖了socket的创建、绑定、连接、监听、接受连接等关键操作,以及数据的读写函数。这些是进行网络通信的基础知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构和字节序

基础数据结构

  • 1基础数据结构和函数:地址 表示数据结构
    struct sockaddr_in{
    _SOCKADDR_COMMON(sin);
    in_port_t sin_port; //端口号
    struct in_addr sin_addr; // IP地址
    }
    典型的填充IP地址的数据结构如下:
struct sockaddr_in addr;

addr.sin_family  =AF_INET;
addr.sinport = htons(80);
addr.sin_addr.s_addr  = inet_addr("192.168.0.1"); 

sin_port 和 sin_addr.s_addr 两个值都是多字节的整数,socket 规定这里必须使
用网络字节序。

网络 字节序和本地字节序的转换

  • 字节序:在网络应用中,字节序是一个必须考虑的因素,因为不同机器类型可能采用不同标准的
    字节序,所以均须按照网络标准转化。网络传输的标准叫做网络字节序,实际上是大端序。而我们常用的 X86 或者 ARM 往往都是小端序。
    手工进行字节序的转换往往是不方便的,对于可移植的程序来说更是如此。总是需要知
    道自己的本地主机字节序也是很麻烦的。所以,系统提供了四个固定的函数,用来在本地字
    节序和网络字节序之间转换。这四个函数包含在头文件<arpa/inet.h>中,分别是:
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong):
uint16_t ntohs(uint16_t netshort);

这四个函数功能依次列举如下:
32 位整数从主机字节序转换为网络字节序;
16 位整数从主机字节序转换为网络字节序;
32 位整数从网络字节序转换为主机字节序;
16 位整数从网络字节序转换为主机字节序。

主机名和地址转换函数

在实际的网络编程中,往往需要在IP地址的点分十进制表示和二进制之间相互 转化,也需要进行主机名和地址的转换,系统提供了一系列的函数,一般需要包含头文件<netinet/in.h>和<arpa/inet.h>
传输中需要二进制,显示时需要十进制

in_addr_t inet_addr(const char *cp)

这个函数将一个点分十进制的 IP 地址字符串转换成 in_addr_t 类型,该类型实际上是一个 32 位无符号整数,事实上就是前文提到的 struct in_addr 结构中的 s_addr 域的数据类型。
注意这个二进制表示的 IP 地址是网络字节序的。这个函数其实在前文举例填充 struct sockaddr_in 的时候用过了。192.168.0.1 在 PC 上会被转换成 0x0100A8C0。

char *inet_ntoa(struct in_addr in)

此函数可以将结构 struct in_addr 中的二进制 IP 地址转换为一个点分十进制表示的字符
串,返回这个字符串的首指针。使用起来很方便。但是要注意,它返回的缓冲区是静态分配
的,在并发或者异步使用时要小心,因为缓冲区随时可能被其它调用改写。

  • 通过主机名获取IP地址
    实际应用中,很多时候得到的是通信另一方的主机名,所以需要将主机名转换为IP地址,传统上有两个函数生命在<netdb.h>中来进行这个 操作。
struct hostent *gethostbyname(const char *name);

直接根据主机名字符串返回一个 struct hostent 结构。

socket常用操作

  • socket的常用操作 主要包括:
  • 创建socket
  • 绑定地址和端口
  • 连接服务器
  • 设置socket为监听模式
  • 接收连接
  • 网络数据的读和写

1.创建socket

创建socket的函数原型:

int socket(int domain, int type, int protocol);

其中,

  • domain代表了socket使用的地址类型(IPV4 IPV6)ipV4使用AF_INET
  • type代表了socket类型(UDP TCP)UDP:SOCK_DGRAM TCP:SOCK_STREAM
  • protocol:协议类型,目前一般为0
    TCP例子:
sock_fd = socket(AF_INET, SOCK_STREAM, 0);

UDP例子:

sock_fd = socket(AF_INET, SOCK_DGRAM, 0);

2.绑定地址和端口

绑定函数原型:

    int bind(int socket,const struct sockaddr *address,socklent address_len);

参数说明:

  • socket指向socket的文件描述符
  • address:指向struct sockaddr 结构的指针,如果填入IP地址,则为struct sockaddr_in 其中的const修饰的是常数,不能变化
  • address_len address内容的长度
    示例:
struct sockaddr_in server_addr;
(void)memset(&server_addr, 0, sock_len);  //从server_addr开始,为连续的n个内存地址赋值0 
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(80);
if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
 perror("bind(2) error");
 goto err;
}

3.连接服务器

函数原型:

int connect(int socket, const struct sockaddr *address, socklent address_len);

TCP示例:

struct sockaddr_in server_addr;
(void)memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(7007);
server_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
if (connect(conn_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
 perror("connect(2) error");
 goto err;
}

4.监听模式

函数原型:

int  listen(int socket,int backlog);
  • socket:指向socket的文件描述符
  • 等待连接的队列长度,实际上队列会大于 这个数字,通常取5

5.接收连接

函数原型:

int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
  • socket:指向socket的文件描述符
  • address:指向struct sockaddr 结构的指针,如果填入IP地址,则为struct sockaddr_in
  • address_len 与前面的函数不同,这里是一个指向 socklen_t 类型的指针,这个存储区域
    用来返回上一个参数返回的地址数据结构的长度
struct sockaddr_in client_addr;
socklen_t sock_len;
„„
while (true) {
 conn_sock = accept(server_sock, (struct sockaddr *)&client_addr, &sock_len);
 if (conn_sock < 0) {
 if (errno == EINTR) {
 /* restart accept(2) when EINTR */
 continue;
 }
 break;
 }
 }

6.读数据函数

  • read(2)、recv(2)、recvfrom(2)和 recvmsg(2)
ssize_t read(int fd, void *buf, size_t count);
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);

对于这四个函数的解析:
write(2)、send(2)、sendto(2)和 sendmsg(2)

ssize_t write(int fd, const void *buf, size_t count);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
 const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

总结之相关操作的函数原型:

int socket(int domain, int type, int protocol);//创建socket
//sock_fd = socket(AF_INET, SOCK_STREAM, 0);
//sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
int bind(int socket, const struct sockaddr *address, socklent address_len);//绑定地址端口
int connect(int socket, const struct sockaddr *address, socklent address_len);//连接
//可以看出,其中的参数完全相同,
//bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))
int listen(int socket, int backlog);//监听模式
int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);//接受连接
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值