首页IT科技linux文件名称长度不能超过(Linux下的高性能轻量级Web服务器(一))

linux文件名称长度不能超过(Linux下的高性能轻量级Web服务器(一))

时间2025-05-04 21:37:56分类IT科技浏览4556
导读:1.让服务器监听客户端的连接请求 1.1 代码块 #...

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)

domain即协议域(族)      ,常用的协议族有AF_INET            、AF_INET6                  、AF_LOCAL(或称AF_UNIX         ,Unix域socket)等等(也可以使用PF前缀                  ,AF_* 和 PF_* 有完全相同的值         ,所以二者经常混用)            。domain决定了socket的地址类型      ,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合      、AF_UNIX决定了要用一个绝对路径名作为地址                  。

  type 为数据传输方式/套接字类型                  ,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)      。两种套接字的区别         。

  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

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

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   。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
微博9宫格切图(给我两分钟的时间:微博风格九宫格:UICollectionView实现) vue使用地图(Vue+ArcGISJavaScriptAPi详解)