socket编程之常用api介绍与socket、select、poll、epoll高并发服务器模型代码实现(2)

简介: 主要API函数介绍socket

主要API函数介绍

socket

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

函数描述: 创建socket

参数说明:

  • domain: 协议版本
函数描述: 创建socket
参数说明:
domain: 协议版本
- - AF_
  • type:协议类型
- - SOCK_STREAM 流式, 默认使用的协议是TCP协议
- - SOCK_DGRAM  报式, 默认使用的是UDP协议
  • protocal:
- - 一般填0, 表示使用对应类型的默认协议.
  • 返回值:
- - 成功: 返回一个大于0的文件描述符
- - 失败: 返回-1, 并设置errno


 当调用socket函数以后, 返回一个文件描述符, 内核会提供与该文件描述符相对应的读和写缓冲区, 同时还有两个队列, 分别是请求连接队列和已连接队列(监听文件描述符才有,listenFd)

84bb513317ae4fd58ac21dce93e1cf6a.png

bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);


函数描述: 将socket文件描述符和IP,PORT绑定

参数说明:


  • socket: 调用socket函数返回的文件描述符
  • addr: 本地服务器的IP地址和PORT,


struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
//serv.sin_addr.s_addr = htonl(INADDR_ANY);
//INADDR_ANY: 表示使用本机任意有效的可用IP
inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
  • addrlen: addr变量的占用的内存大小


返回值:


  • 成功: 返回0
  • 失败: 返回-1, 并设置errno


listen

int listen(int sockfd, int backlog);

函数描述: 将套接字由主动态变为被动态

参数说明:

  • sockfd: 调用socket函数返回的文件描述符
  • backlog: 在linux系统中,这里代表全连接队列(已连接队列)的数量。在unix系统种,这里代表全连接队列(已连接队列)+ 半连接队列(请求连接队列)的总数


返回值:


  • 成功: 返回0
  • 失败: 返回-1, 并设置errno


accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);  

函数说明:获得一个连接, 若当前没有连接则会阻塞等待.

函数参数:


  • ockfd: 调用socket函数返回的文件描述符
  • addr: 传出参数, 保存客户端的地址信息
  • addrlen: 传入传出参数, addr变量所占内存空间大小


返回值:


  • 成功: 返回一个新的文件描述符,用于和客户端通信
  • 失败: 返回-1, 并设置errno值.


accept函数是一个阻塞函数, 若没有新的连接请求, 则一直阻塞.

  从已连接队列中获取一个新的连接, 并获得一个新的文件描述符, 该文件描述符用于和客户端通信. (内核会负责将请求队列中的连接拿到已连接队列中)

connect

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);


函数说明: 连接服务器

函数参数:


  • sockfd: 调用socket函数返回的文件描述符
  • addr: 服务端的地址信息
  • addrlen: addr变量的内存大小


返回值:

  • 成功: 返回0
  • 失败: 返回-1, 并设置errno值


读取和发送数据

接下来就可以使用write和read函数进行读写操作了。除了使用read/write函数以外, 还可以使用recv和send函数。

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags); 
//对应recv和send这两个函数flags直接填0就可以了


注意: 如果写缓冲区已满, write也会阻塞, read读操作的时候, 若读缓冲区没有数据会引起阻塞。


高并发服务器模型-select

select介绍

  多路IO技术: select, 同时监听多个文件描述符, 将监控的操作交给内核去处理

int select(int nfds, fd_set * readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);


数据类型fd_set::文件描述符集合——本质是位图

函数介绍: 委托内核监控该文件描述符对应的读,写或者错误事件的发生

参数说明:


  • nfds: 最大的文件描述符+1
  • readfds: 读集合, 是一个传入传出参数

    传入: 指的是告诉内核哪些文件描述符需要监控
    传出: 指的是内核告诉应用程序哪些文件描述符发生了变化
  • writefds: 写文件描述符集合(传入传出参数,同上)
  • execptfds: 异常文件描述符集合(传入传出参数,同上)
  • timeout:

    NULL--表示永久阻塞, 直到有事件发生
    0   --表示不阻塞, 立刻返回, 不管是否有监控的事件发生
    >0  --到指定事件或者有事件发生了就返回
  • 返回值: 成功返回发生变化的文件描述符的个数。失败返回-1, 并设置errno值。


select-api

将fd从set集合中清除


void FD_CLR(int fd, fd_set *set);

功能描述: 判断fd是否在集合中

返回值: 如果fd在set集合中, 返回1, 否则返回0

int FD_ISSET(int fd, fd_set *set);

将fd设置到set集合中

void FD_SET(int fd, fd_set *set);

初始化set集合

void FD_ZERO(fd_set *set);

用select函数其实就是委托内核帮我们去检测哪些文件描述符有可读数据,可写,错误发生

int select(int nfds, fd_set * readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

select优缺点

select优点:

  1. select支持跨平台

select缺点:


1.代码编写困难

2.会涉及到用户区到内核区的来回拷贝

3.当客户端多个连接, 但少数活跃的情况, select效率较低(例如: 作为极端的一种情况, 3-1023文件描述符全部打开, 但是只有1023有发送数据, select就显得效率低下)

4.最大支持1024个客户端连接(select最大支持1024个客户端连接不是有文件描述符表最多可以支持1024个文件描述符限制的, 而是由FD_SETSIZE=1024限制的)


FD_SETSIZE=1024 fd_set使用了该宏, 当然可以修改内核, 然后再重新编译内核, 一般不建议这么做

select代码实现

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <pthread.h>
#define MAX_LEN  4096
int main(int argc, char **argv) {
    int listenfd, connfd, n;
    struct sockaddr_in svr_addr;
    char buff[MAX_LEN];
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    memset(&svr_addr, 0, sizeof(svr_addr));
    svr_addr.sin_family = AF_INET;
    svr_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    svr_addr.sin_port = htons(8081);
    if (bind(listenfd, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    //select
    fd_set rfds, rset, wfds, wset;
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);
    FD_SET(listenfd, &rfds);
    int max_fd = listenfd;
    while (1) {
        rset = rfds;
        wset = wfds;
        int nready = select(max_fd + 1, &rset, &wset, NULL, NULL);
        if (FD_ISSET(listenfd, &rset)) { //
            struct sockaddr_in clt_addr;
            socklen_t len = sizeof(clt_addr);
            if ((connfd = accept(listenfd, (struct sockaddr *) &clt_addr, &len)) == -1) {
                printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                return 0;
            }
            FD_SET(connfd, &rfds);
            if (connfd > max_fd) max_fd = connfd;
            if (--nready == 0) continue;
        }
        int i = 0;
        for (i = listenfd + 1; i <= max_fd; i++) {
            if (FD_ISSET(i, &rset)) { //
                n = recv(i, buff, MAX_LEN, 0);
                if (n > 0) {
                    buff[n] = '\0';
                    printf("recv msg from client: %s\n", buff);
                    FD_SET(i, &wfds);
                }
                else if (n == 0) { //
                    FD_CLR(i, &rfds);
                    close(i);
                }
                if (--nready == 0) break;
            }
            else if (FD_ISSET(i, &wset)) {
                send(i, buff, n, 0);
                FD_SET(i, &rfds);
                FD_CLR(i, &wfds);
            }
        }
    }
    close(listenfd);
    return 0;
}

目录
相关文章
|
10月前
|
JSON 监控 网络协议
干货分享“对接的 API 总是不稳定,网络分层模型” 看电商 API 故障的本质
本文从 OSI 七层网络模型出发,深入剖析电商 API 不稳定的根本原因,涵盖物理层到应用层的典型故障与解决方案,结合阿里、京东等大厂架构,详解如何构建高稳定性的电商 API 通信体系。
|
7月前
|
Shell 网络安全 开发工具
服务器已经搭建好的项目如何关联至gitee对应仓库并且将服务器的项目代码推送至gitee-优雅草卓伊凡
服务器已经搭建好的项目如何关联至gitee对应仓库并且将服务器的项目代码推送至gitee-优雅草卓伊凡
437 5
|
自然语言处理 安全 API
API First:模型驱动的阿里云API保障体系
本文介绍了阿里云在API设计和管理方面的最佳实践。首先,通过API First和模型驱动的方式确保API的安全、稳定和效率。其次,分享了阿里云内部如何使用CloudSpec IDL语言及配套工具保障API质量,并实现自动化生成多语言SDK等工具。接着,描述了API从设计到上线的完整生命周期,包括规范校验、企业级能力接入、测试和发布等环节。最后,展望了未来,强调了持续提升API质量和开源CloudSpec IDL的重要性,以促进社区共建更好的API生态。
|
8月前
|
JSON API 调度
Midjourney 技术拆解与阿里云开发者实战指南:从扩散模型到 API 批量生成
Midjourney深度解析:基于优化Stable Diffusion,实现文本到图像高效生成。涵盖技术架构、扩散模型原理、API调用、批量生成系统及阿里云生态协同,助力开发者快速落地AIGC图像创作。
1030 0
|
8月前
|
人工智能 API 监控
告别多接口拼凑!阿里云 API 模型聚合实现技术能力协同跃迁
API聚合整合400+国内外AI模型,统一接口、屏蔽差异,降低开发与维护成本,提升效率与系统稳定性,助力开发者高效应对多API调用困境。
857 0
|
8月前
|
人工智能 前端开发 测试技术
Kimi K2 模型更新,带来更强的代码能力、更快的 API
今天,Kimi K2 模型的最新版本 0905 开源发布,进一步提升其在真实编程任务中的表现
1791 0
|
10月前
|
API
本地用阿里云API调用的r1模型,返回的think字段中有奇怪的东西,并且停止思考
这两张图片展示了模型生成内容时可能出现的异常情况,包括图像模糊、结构错误或不符合预期的结果。这可能是由于模型训练数据不足、输入指令不清晰或模型本身存在局限性所致。建议优化输入提示词或调整模型参数以提升输出质量。
|
存储 人工智能 API
AgentScope:阿里开源多智能体低代码开发平台,支持一键导出源码、多种模型API和本地模型部署
AgentScope是阿里巴巴集团开源的多智能体开发平台,旨在帮助开发者轻松构建和部署多智能体应用。该平台提供分布式支持,内置多种模型API和本地模型部署选项,支持多模态数据处理。
12386 78
AgentScope:阿里开源多智能体低代码开发平台,支持一键导出源码、多种模型API和本地模型部署
|
10月前
|
数据采集 监控 网络协议
基于aiohttp的高并发爬虫实战:从原理到代码的完整指南
在数据驱动时代,传统同步爬虫效率低下,而基于Python的aiohttp库可构建高并发异步爬虫。本文通过实战案例解析aiohttp的核心组件与优化策略,包括信号量控制、连接池复用、异常处理等,并探讨代理集成、分布式架构及反爬应对方案,助你打造高性能、稳定可靠的网络爬虫系统。
778 0