首页 > 系统相关 >Linux下的高性能轻量级Web服务器(一)

Linux下的高性能轻量级Web服务器(一)

时间:2023-02-17 15:35:30浏览次数:55  
标签:Web socket sockaddr int 地址 Linux addr 轻量级 字节

1.让服务器监听客户端的连接请求

1.1 代码块

#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include<stdio.h>
#include<stdlib.h>

#define BUFFER_LEN 1024
#define Max_Client_Num 32
#define port 8080

int main()
{
    /* 创建监听socket文件描述符 */
    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    if(listenfd == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* 创建监听socket的TCP/IP的IPV4 socket地址 */
    struct sockaddr_in address;
    /* 将address指向的内存块清零 */
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl(INADDR_ANY);  /* INADDR_ANY:将套接字绑定到所有可用的接口 */
    address.sin_port = htons(port); /* port:端口号 */

    int flag = 1;
    /* setsockopt设置socket选项,SO_REUSEADDR 允许端口被重复使用 */
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
    /* 绑定socket和它的地址 */
    if (bind(listenfd, (struct sockaddr*)&address, sizeof(address)) == -1)
    {
        perror("bind");
        return -1;
    }
    /* 创建监听队列以存放待处理的客户连接,在这些客户连接被accept()之前 */
    if (listen(listenfd, Max_Client_Num))
    {
        perror("listen");
        return -1;
    }

    return 0;
}

socket函数:int socket(int domain, int type, int protocol)

  1. domain即协议域(族),常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)等等(也可以使用PF前缀,AF_* 和 PF_* 有完全相同的值,所以二者经常混用)。domain决定了socket的地址类型,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
  2. type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)。两种套接字的区别
  3. protocol 表示传输协议,是在前两个参数构成的协议集合下,再选择一个具体的传输协议,这个值通常都是唯一的(前两个参数已经完全决定了它的值)。几乎在所有情况下,我们都应该把它设置为0,表示使用默认值(让系统自动推演出使用哪种协议)。

    该函数成功时返回一个socket文件描述符,失败则返回-1并设置errno。

socket地址

1)理解socket地址
   假设小明想给女神打电话,需要知道对方的电话号码才能进行沟通,而我们进行网络通信也需要先知道对方的socket地址。
   在网络通信中,socket地址最关键的两部分为:(ip,port),即IP地址和port端口号,比如一个网络地址为210.177.200.192:8000, 通过IP地址210.177.200.192在网络中找到该计算机,再通过端口号8000与该计算机上对应的应用程序进行通信。

2)通用socket地址
    socket网络编程用接口中表示socket地址的是结构体sockaddr,其定义如下:
#include <bits/socket.h>
struct sockaddr
{
    sa_family_t sa_family; /* sa_family是地址族类型的变量 */
    char sa_data[14]; /* 14字节,存放socket地址值,ip地址和端口号 */
};
    因为14字节的sa_data无法完全容纳多数协议族的地址值。因此,Linux定义了新的通用socket地址结构体:
struct sockaddr_storage
{
    sa_family_t sa_family; // 地址族
     unsigned long int __ss_align; // 用于内存对齐
    char __ss_padding[128-sizeof(__ss_align)]; // 提供足够大的空间用于存放地址值
};
    但一般不直接用通用socket结构体,因为用的会蛋疼,需要执行繁琐的位操作。
注意: sockaddr_storage结构体往往用于事先不知道地址族的类型这一情况,因为它能够承载系统支持的任何socket地址结构体。

3)专用socket地址
    因为通用socket地址非常不好使用,所以Linux为各个协议族提供了专门的socket地址结构体。
UNIX本地域协议族使用如下专用socket地址结构体:
#include <sys/un.h>
struct sockaddr_un
{
    sa_family_t sin_family; /* 地址族:AF_UNIX */
    char sun_path[108]; /* 文件路径名 */
};
TCP/IP协议族有sockaddr_insockaddr_in6两个专用socket地址结构体,它们分别用于IPv4和IPv6:
struct sockaddr_in
{
    sa_family_t sin_family; /* 地址族:AF_INET */
    u_int16_t sin_port; /* 端口号,要用网络字节序表示 */
    struct in_addr sin_addr; /* IPv4地址结构体,见下面 */
};
struct in_addr
{
    u_int32_t s_addr; /* IPv4地址,要用网络字节序表示 */
};

struct sockaddr_in6
{
    sa_family_t sin6_family; /* 地址族:AF_INET6 */
    u_int16_t sin6_port; /* 端口号,要用网络字节序表示 */
    struct in6_addr sin6_addr; /* IPv6地址结构体 */
    u_int32_t sin6_flowinfo; /* 流信息,应设置为0 */
    u_int32_t sin6_scope_id; /* scope ID */
};
struct in6_addr
{
    unsigned char sa_addr[16]; /* IPv6地址,要用网络字节序表示 */
};
注意:所有专用socket地址(以及sockaddr_storage)类型的变量在实际使用时都需要转化为通用socket地址类型sockaddr(强制转换即可),因为所有socket编程接口使用的地址参数的类型都是sockaddr。

socket选项:int setsockopt( int socket, int level, int option_name,const void *option_value, socklen_t ,option_len)

sockfd参数指定被操作的目标socket。level参数指定要操作哪个协议的选项,比如IPv4、IPv6、TCP等。option_value参数则指定选项的名字。常用的选项有SO_REUSEADDR、SO_RCVBUF、SO_SNDBUF、SO_RCVLOWAT、SO_SNDLOWAT、SO_LINGER选项。option_valueoption_len参数分别是被操作选项的值和长度。

htonl(), ntohl(), htons(), ntohs() 函数

了解这四个函数功能前,应先知道什么是大端/小端字节序。例如一个int类型的数占4字节(可以理解为由四部分二进制数组成了这个int类型的数),这四部分在内存中的排列顺序就是字节序问题。字节序分为大端字节序(big endian)和小端字节序(little endian)。
大端字节序是指一个整数的高位字节(23 ~ 31bit)存储在内存的低地址处,低位字节(0 ~ 7bit)存储在内存的高地址处。小端字节序则相反,高位字节存储在内存的高地址处,低位字节存储在内存的低地址处。
例如 0x1234567 的大端字节序和小端字节序如下图所示:

在两台使用不同字节序的主机之间直接传递时,接收端必然会错误的解释信息。解决办法就:发送端总把数据转化为大端字节序数据后再发送,而接收端可根据自身的字节序决定是否对接收的数据进行转换。
Linux提供了以下四个函数来完成大端字节序(也叫网络字节序)和小端字节序(主机字节序)的转换:
unsigned long int htonl(unsigned lont int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned lont int netlong);
unsigned short int ntohs(unsigned short int netshort);
它们的含义非常明确,例如htonl表示“ host to network long ” ,即将长整型(32bit)的主机字节序转化为网络字节序。长整型函数通常用来转换IP地址,短整型函数用来转换端口号(任何格式化的数据通过网络传输时,都应该使用这些函数来转换字节序)。

bind函数:int bind ( int sockfd, const struct sockaddr *addr, socklen_t addrlen )

    将一个socket与socket地址绑定称为给socket命名。在服务器程序中,通常需要命名socket,因为只有命名后客户端才能知道如何连接它。客户端则通常不需要命名socket,而是采用匿名的方式,即使用操作系统自动分配的socket地址。
    命名socket的方法则是采用bind函数,bind将addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出该socket地址的长度。
    bind成功时返回0,失败时返回-1并设置errno。其中常见的errno是EACCES和EADDRINUSE。
    EACCES表示被绑定的地址是受保护的地址,仅超级用户能够访问,比如普通用户将socket绑定到知名服务端口(0~1023)上。
    EADDRINUSE表示被绑定的地址正在使用中。

listen函数:int listen ( int sockfd, int backlog )

    socket被命名后,还不能马上接收客户连接,我们需要使用listen函数来创建一个监听队列以存放待处理的客户连接。
    sockfd参数指定被监听的socket。backlog参数表示监听队列的最大长度。监听队列的长度如果超过backlog,服务器将不受理新的客户连接,客户端也会收到ECONNREFUSED错误信息。在Linux内核>2.2之后的版本中,backlog只表示处于完全连接状态的socket的上限。
    listen成功时返回0,失败则返回-1并设置errno。

标签:Web,socket,sockaddr,int,地址,Linux,addr,轻量级,字节
From: https://www.cnblogs.com/zyzhi/p/WebServer1.html

相关文章

  • 【IMX6ULL学习笔记】九、Linux内核移植
    一、在Linux中添加自己的开发板1、添加开发板默认配置文件将arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份,命名为imx_kodo_emmc_defconfig,命令如......
  • Linux基础实训(利用线程和互斥锁)
                         实验要求(linux)1定义一个长度为8的数组2输入不同的8个(大写)字母(ASDFGHJK)3对字符串进行排序4用线程和互斥锁输出按顺......
  • Linux如何查看系统和进程的运行状态?
    对于运维人员来讲,监控服务器的运行状态是他们的必要工作,而Linux系统提供了很多关于服务器运行状态的命令,那么Linux如何查看系统和进程的运行状态?可以使用top命令,接下来......
  • Vue CLI 2内置框架webpack框架结构解析
    目前Vue已经到3.X版本,相应的VueCLI也已经是VueCLI3版本,创建命令使用vuecreate,如果要用2.X版的vueinit命令,需要全局安装一个桥接工具:npminstall-g@vue/cli-init创......
  • linux下安装nginx
    1.首先去官网下载安装包  链接:http://nginx.org/en/download.html    或者直接在linux下使用wget进行下载。  首先命令行查看有没有wget,没有则下载:yumins......
  • curl post请求发送json数据两种方式(Window/Linux)
    curlpost请求发送json数据两种方式(Window/Linux) 设置请求头Content-Typecurl发送post请求,默认的content-type是:application/x-www-form-urlencoded。要发送json格式,......
  • Java-webshell 排查
    参考:https://javasec.org/javaweb/MemoryShell/https://goodapple.top/archives/1355简介本次分享为javawebshell排查初级。抛砖引玉java获取web权限的shell......
  • 4. JWT(JSON Web Token)鉴权
    通过第三节,知道了Token的使用方式以及组成,不难发现,服务端验证客户端发送过来的Token时,还需要查询数据库获取用户基本信息,然后验证Token是否有效;这样每次请求验证都......
  • 【Linux系统】Centos7系统下配置samba共享
    (【Linux系统】Centos7系统下配置samba共享)一、Samba服务介绍Samba是一套使用SMB(ServerMessageBlock)协议的应用程序,通过支持这个协议,Samba允许Linux服务器与Win......
  • linux源码解析12–page数据结构
    几个问题:1.当开启了MMU之后,CPU访问内存的最小单位是多少呢?page2.linux怎样描述这个页呢?3.linux内核里,怎么理解和使用这个页?linux内核用stuctpage来描述一个物理页面:1......