网络编程

    科技2024-10-13  32

    目录

    进程间通信地址结构体sockaddr_in几个转换函数1.端口 阻塞编程函数1.socket2.bind3. listen4.connect5.accept tcp状态转换图多路IO转接编程write()与read()函数文件描述符集合函数select()函数


    进程间通信

    地址结构体sockaddr_in

    struct sockaddr_in{ sa_family_t sin_family; //地址族(Address Family),也就是地址类型 uint16_t sin_port; //16位的端口号 struct in_addr sin_addr; //32位IP地址 char sin_zero[8]; //不使用,一般用0填充 };

    sin_family 代表IP地址类型,AF_INET 和 AF_INET6,和 socket() 的第一个参数的含义相同,取值也要保持一致。

    sin_port 为端口号。uint16_t 的长度为两个字节,理论上端口号的取值范围为 0~65536,但 0~1023 的端口一般由系统分配给特定的服务程序,例如 Web 服务的端口号为 80,FTP 服务的端口号为 21,所以我们的程序要尽量在 1024~65536 之间分配端口号。端口号需要用 htons() 函数转换。

    sin_addr 是 struct in_addr 结构体类型的变量,其中包含一个名为s_addr的in_addr_t类型成员。

    sin_zero[8] 是多余的8个字节,没有用,一般使用 memset() 函数填充为 0。上面的代码中,先用 memset(&addr,0,sizeof(addr)) 函数将结构体的全部字节填充为 0,再给前3个成员赋值,剩下的 sin_zero 自然就是 0 了。

    几个转换函数

    1.端口

    htons(“127.0.0.1”)

    阻塞编程函数

    1.socket

    int socket(int af, int type, int protocol); 使用 socket() 函数创建套接字以后,返回值就是一个 int 类型的套接字文件描述符。

    af 为地址族(Address Family),也就是 IP 地址类型,取值为 AF_INET 和 AF_INET6type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。操作系统会自动推演出协议类型,可以默认为0。

    2.bind

    int bind(int sock, struct sockaddr addr, socklen_t addrlen); 作用时绑定套接字与ip地址,参数sock 为 socket 文件描述符; 参数addr 为 sockaddr 结构体变量的指针,需要将其取地址后加上(sockaddr)强转; addrlen 为 addr 变量的大小,可由 sizeof() 计算得出

    3. listen

    int listen(int sock, int ) sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。 当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列 listen() 函数只是让套接字处于被动监听状态,并没有接收请求,接收请求需要使用 accept() 函数。

    4.connect

    int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen); 写在客户端中,用于向服务端发起连接请求,参数与bind相同,serv_addr是服务器的IP结构体。

    5.accept

    int accept(int sock, struct sockaddr *clientAddr, socklen_t *addrlen); 当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。函数返回值为客户端的套接字。clientAddr中保存客户端的IP。

    tcp状态转换图

    多路IO转接编程

    write()与read()函数

    使用open函数打开文件的目的在于获得该文件的文件描述符,使用socket()函数得到的文件描述符也是如此。read和write函数是对文件进行读写操作的函数。头文件为<unistd.h>

    ssize_t read(int fd,void*buf,size_t count)

    read()函数用于文件描述符对应的文件中读取数据。成功返回读出的字节数 失败返回-1,并设置errno,如果在调用read之前到达文件末尾,则这次read返回0。其中参数: fd: 是文件描述符, 从command line获取数据时,为0 buf: 为读出数据的存储位置; count: 为每次读取的字节数(是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移)

    ssize_t write(int fd,const void*buf,size_t count);

    write()函数用于将数据写入到文件描述符对应的文件.成功返回写入的字节数,等于请求写的字节数count,失败返回-1并设置errno。其中参数: fd:是文件描述符(输出到command line,就是1) buf:通常是一个字符串,需要写入的字符串 count:是每次写入的字节数

    文件描述符集合函数

    void FD_ZERO(fd_set *set); 清空一个文件描述符集合,例:

    fd_set set;//定义一个一个文件描述符集合 FD_ZERO(&set)

    void FD_SET(int fd, fd_set *set) 将待监听的文件描述符,添加到监听的集合中

    FD_SET(3, &set); FD_SET(5, &set); FD_SET(7, &set);

    void FD_CLR(int fd, fd_set * set); 将一个文件描述符从监听集合中移除

    FD_CLR(3, &set);

    int FD_ISSET(int fd, fd_set *set); 判断一个文件描述符是否在监听集合中被置位为1,是就返回1,否则返回0。

    select()函数

    在高并发服务器中,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。

    从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

    函数原型: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 返回值: 返回大于0时,返回所有监听集合中,满足对应事件的总数 返回0时,没有满足监听条件的文件描述符 返回-1时,出现错误

    参数:

    nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错。在linux系统中,select的默认最大值为1024。设置这个值的目的是为了不用每次都去轮询这1024个fd,假设我们只需要几个套接字,我们就可以用最大的那个套接字的值加上1作为这个参数的值,当我们在等待是否有套接字准备就绪时,只需要监测maxfd+1个套接字就可以了,这样可以减少轮询时间以及系统的开销。readfds:读文件描述符监听集合。首先需要明白,fd_set是什么数据类型,有一点像int,又有点像struct,其实,fd_set声明的是一个集合,也就是说,readfs是一个容器,里面可以容纳多个文件描述符,把需要监视的描述符放入这个集合中,当有文件描述符可读时,select就会返回一个大于0的值,表示有文件可读writefds:写文件描述符集合,如果没用则设置为NULL,当有文件可写时,select就会返回一个大于0的值,表示有文件可写;exceptfds:异常文件描述符监听集合,如果没用则设置为NULL,同上面两个参数的意图,用来监视文件错误异常文件。timeout:这个参数用于设置可以选择阻塞,可以选择非阻塞,还可以选择定时返回。 当将timeout置为NULL时,表明此时select是阻塞的; 当将tineout设置为timeout.tv_sec = 0,timeout.tv_usec = 0时,表明这个函数为非阻塞; 当将timeout设置为非0的时间,表明select有超时时间,当这个时间走完,select函数就会返回。

    解释

    struct fd_set可以理解为一个集合,这个集合中存放的是 文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些函数由人为来操作 struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,tv_sec是秒数,tv_usec是微秒数。

    Processed: 0.012, SQL: 8