网络编程-数据结构与函数详解

网络编程-一个简单的客户端与服务器程序(0),(看这篇就够了)中已经对程序整体有了宏观的认识。这篇主要对数据结构与函数详解。

数据结构与函数详解

IPv4相关结构:

struct in_addr
{
    in_addr_t          s_addr;  //表示32位的IP地址,32位无符号整型
}

struct sockaddr_in
{
    uint8_t            sin_len;       //表示该结构体的长度,8位无符号整型
    sa_family_t        sin_family;    //表示套接口使用的协议族,8位无符号整型
    in_port_t          sin_port;      //表示套接口使用的端口号,16位无符号整型
    struct in_addr     sin_addr;      //表示IP地址,32位无符号整型
    char               sin_zero[8];   //该成员基本不使用,总是置为0
}

sockaddr_in是IPV4套接字地址结构。

sin_len 成员是不要求一定存在的,即便这个成员存在,也无需设置它或者检查它。换句话说就是一般情况下,我们用不到该成员。

这三个sin_family、sin_port、sin_addr成员是必须存在的,分别表示套接字使用的协议族、端口号、还有IP地址。

sin_zero 该成员不使用,总是设置为0

htonl/htons/ntohl/ntohs

本地序(也称主机序)即指处理器本身所采用的字节序,因此有的大端序,有的小端序。而网络序,是指网络传输采用的字节序。所幸,网络序是标准化的,即一般统一采用大端序。因此,发送网络数据之前需要将数据转换为网络序。

htonl、htons用于本地序转网络序。
ntohl、ntohs用于网络序转本地序。

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

例子:

网络字节是0x0600,而在主机内部所读的字节是0x06.

简单而言,htons()是将一个数的高低位进行互换:(如:06 00 --> 00 06)

inet_ntop/inet_pton

#include <arpa/inet.h>

const char *inet_ntop(int af, const void *restrict src,
       char *restrict dst, socklen_t size);
int inet_pton(int af, const char *restrict src, void *restrict dst);

inet_ntop()函数将一个数字地址转换成一个适合显示的文本字符串。
inet_pton()函数应该将标准文本表示形式的地址转换为数字二进制形式。

af参数根据协议族的不同,选择AF_INET或AF_INET6
src参数指向的字符串,转为数值,存放在addstr指向的内存中

inet_pton/inet_ntop分别用于将字符串ip地址转为4字节大小的无符号整型和将无符号整型转换为ip地址字符串。例子为

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

int main(int argc, char *argv[]){

    char ip[] = "192.168.0.114"; 
    struct in_addr addr;
    
    int ret = inet_pton(AF_INET, ip, (void *)&addr);   //IP字符串 ——>网络字节流
    if(0 == ret){
        printf("inet_pton error, return 0\n");
        return -1;
    }else{
        printf("inet_pton ip: %ld\n", addr.s_addr);
        printf("inet_pton ip: 0x%x\n", addr.s_addr);
    }

    const char *pstr = inet_ntop(AF_INET, (void *)&addr, ip, sizeof(ip));  //网络字节流 ——>IP字符串
    if(NULL == pstr){
        printf("inet_ntop error, return NULL\n");
        return -1;
    }else{
        printf("inet_ntop ip: %s\n", ip);
    }
    
    return 0;
}

编译运行:
在这里插入图片描述
从运行结果中可以清晰看到两者之间的转换。

socket 函数

#include <sys/socket.h>

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

socket()函数应该在一个通信域中创建一个未绑定的套接字,并返回一个文件描述符,该文件描述符可以在以后对套接字进行操作的函数调用中使用。文件描述符应按文件描述符分配中描述的方式分配。

domain:

协议域,决定了 socket 的地址类型,在通信中必须采用对应的地址。常用的协议族有:AF_INET(ipv4地址与端口号的组合)、AF_INET6(ipv6地址与端口号的组合)、AF_LOCAL(绝对路径名作为地址)。

type:

常用的类型有:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。

其中 SOCK_STREAM 表示提供面向连接的稳定数据传输,即 TCP 协议。SOCK_DGRAM表示数据报套接字,即UDP协议

protocol:

常用的协议有:IPPROTO_TCP(TCP协议)、IPPTOTO_UDP(UDP协议)、IPPROTO_SCTP(STCP协议)。

当值位 0 时,会自动选择 type 类型对应的默认协议。

socket 起源于 UNIX,而 UNIX/Linux 基本哲学之一就是「一切皆文件」,socket 即是一种特殊的文件,一些 socket 函数就是对其进行的操作。 都可以用「open → write/read → close」模式来操作。

bind 函数

调用socket函数之后已经确定了协议族和传输协议,但是还没有确定本地协议,即套接字地址信息。

#include <sys/socket.h>

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

socket:

即 socket 描述字,由 socket() 函数创建。

*address:

一个 const struct sockaddr 指针,指向要绑定给 sockfd 的协议地址。
这个地址结构根据地址创建 socket 时的地址协议族不同而不同,例如 ipv4 对应 sockaddr_in,ipv6 对应 sockaddr_in6

这几个结构体在使用的时候,都可以强制转换成 sockaddr。
下面是这几个结构体对应的所在的头文件:

1、sockaddr: sys/socket.h
2、sockaddr_in: netinet/in.h
3、sockaddr_in6: netinet6/in.h

_in 后缀意义:互联网络(internet)的缩写,而不是输入(input)的缩写。

listen 函数

服务器调用,该套接字可以接收来自客户端的连接请求。

#include <sys/socket.h>

int listen(int socket, int backlog);

socket:

即 socket 描述字,由 socket() 函数创建。

backlog:

指定在请求队列中的最大请求数,进入的连接请求将在队列中等待 accept() 它们。

connect 函数

由客户端调用,与目的服务器的套接字建立一个连接。

#include <sys/socket.h>

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

socket:

目的服务器的 socket 描述符 。

*address

一个 const struct sockaddr 指针,包含了目的服务器 IP 和端口。

address_len:

协议地址的长度,如果是 ipv4 的 TCP 连接,一般为 sizeof(sockaddr_in);

accept 函数

accept函数在服务端调用,它用于接受来自客户端的连接,从已完成连接队列返回一个已完成连接。

#include <sys/socket.h>

int accept(int socket, struct sockaddr *restrict address,
       socklen_t *restrict address_len);

socket:

服务器的 socket 描述字,由 socket() 函数创建。

*restrict address:

一个 struct sockaddr 指针,用来存放提出连接请求客户端的主机的信息

*restrict address_len:

协议地址的长度,如果是 ipv4 的 TCP 连接,一般为 sizeof(sockaddr_in)。

close 函数

在数据传输完成之后,手动关闭连接。

#include <unistd.h>

int close(int fildes);

fd:

需要关闭的连接 socket 描述符 。

网络 I/O 函数

当客户端和服务器建立连接后,可以使用网络 I/O 进行读写操作。
网络 I/O 操作有下面几组:

read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()

最常用的是 read()/write()

ssize_t read(int fildes, void *buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte);

鉴于该文是侧重于描述 数据结构与函数详解的工作原理,就不再详细描述这些函数了。

在这里插入图片描述
(微信公众号【程序猿编码】)

在这里插入图片描述

(添加本人微信号,备注加群,进入程序猿编码交流群,领取学习资料,获取每日干货)

微信公众号【程序猿编码】,这里Linux c/c++ 、Go语言、数据结构与算法、网络编程相关知识,常用的程序员工具。还有汇聚精炼每日时政、民生、文化、娱乐新闻简报,即刻知晓天下事!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值