IO多路复用小项目day01 ———— c语言版本
// 1. 创建监听的fd
int lfd = socket(AF_INET,SOCK_STREAM,0);
在 C 语言中使用套接字(socket)编程时,socket 函数是用于创建一个新的套接字描述符的关键函数。这个函数在 <sys/socket.h> 头文件中定义,并且通常需要三个参数来指定套接字的类型、地址族和协议。下面是 socket(AF_INET, SOCK_STREAM, 0) 中每个参数的详细讲解:
- 地址族(address family):
AF_INETAF_INET表示使用 IPv4 地址族。IPv4 是目前广泛使用的互联网协议版本 4,它使用 32 位的地址来表示网络上的设备。- 还有其他地址族,如
AF_INET6表示 IPv6(使用 128 位地址),AF_UNIX表示 Unix 域套接字(用于在同一台机器上的不同进程之间的通信)。
- 套接字类型(socket type):
SOCK_STREAMSOCK_STREAM表示创建一个面向连接的、可靠的字节流套接字。这种套接字类型通常用于 TCP(传输控制协议)通信。- 另一种常见的套接字类型是
SOCK_DGRAM,它表示创建一个无连接的、不可靠的数据报套接字,通常用于 UDP(用户数据报协议)通信。
- 协议(protocol):
0- 第三个参数通常用于指定特定的协议。然而,当设置为
0时,系统会根据前两个参数(地址族和套接字类型)自动选择最合适的协议。 - 对于
AF_INET和SOCK_STREAM的组合,系统通常会选择 TCP 协议。 - 同样地,对于
AF_INET和SOCK_DGRAM的组合,系统通常会选择 UDP 协议。
- 第三个参数通常用于指定特定的协议。然而,当设置为
综上所述,socket(AF_INET, SOCK_STREAM, 0) 这行代码的作用是创建一个使用 IPv4 地址族、面向连接的 TCP 协议的套接字描述符,并将这个描述符存储在变量 lfd 中。这个套接字描述符随后可以用于绑定到特定的地址和端口、监听连接请求、接受连接以及进行数据的发送和接收。
// 2. 设置端口复用
int opt = 1;
int ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
if (ret == -1)
{
perror("setsockopt");
return -1;
}
知识点讲解
-
端口复用(SO_REUSEADDR):
-
端口复用允许在同一端口上绑定多个套接字,前提是每次绑定时所用的地址(通常是IP地址和端口的组合)或套接字的其他属性(如协议)有所不同。
-
当一个服务器程序终止后,操作系统通常会保留一段时间(称为“TIME_WAIT”状态)该程序所使用的端口,以防止由于网络延迟导致的迟到的数据包被新启动的、使用同一端口的程序错误地接收。
-
通过设置
SO_REUSEADDR选项,可以使得在程序终止后立即重启程序时,能够成功绑定到之前使用的端口,而不会因为端口处于“TIME_WAIT”状态而失败。
-
-
setsockopt函数:-
setsockopt函数用于设置套接字描述符的选项。 -
语法:
int setsockopt(int sockfd, int level, int option_name, const void *option_value, socklen_t option_len); -
参数:
-
sockfd:要设置选项的套接字描述符(在本例中是lfd)。 -
level:指明选项的级别。对于套接字层级的选项,通常使用SOL_SOCKET。 -
option_name:具体要设置的选项名,本例中是SO_REUSEADDR。 -
option_value:指向包含所需选项值的缓冲区。本例中是一个整数1,表示启用端口复用。 -
option_len:option_value所指缓冲区的长度,本例中是sizeof(opt),即sizeof(int),因为opt是一个整数。
-
-
-
错误处理:
-
setsockopt函数返回0表示成功,返回-1表示失败。 -
如果
setsockopt返回-1,则使用perror函数打印出错误信息,并返回-1以指示上层调用者发生了错误。 -
perror函数会根据全局变量errno的值打印出相应的错误信息。errno是在调用系统函数(如setsockopt)失败时被设置的,用于指示错误的具体原因。
-
总结
上述代码段通过设置SO_REUSEADDR选项来启用端口复用功能,以便在程序重启时能够立即绑定到之前使用的端口。如果设置选项失败,代码会打印出错误信息并返回-1。这是网络编程中常见的套接字选项设置之一,用于提高程序的健壮性和可用性。
// 3. 绑定
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(lfd, (struct sockaddr*)&addr, sizeof addr);
if (ret == -1)
{
perror("bind");
return -1;
}
知识点讲解
-
结构体
sockaddr_in:-
sockaddr_in是用于描述 IPv4 套接字地址的结构体。 -
它包含了地址族(
sin_family)、端口号(sin_port)和 IP 地址(sin_addr)等信息。
-
-
地址族
AF_INET:-
addr.sin_family = AF_INET;表示我们使用的是 IPv4 地址族。
-
-
端口号
htons(port):-
htons函数用于将主机字节序的端口号转换为网络字节序。 -
port是一个整数,表示我们想要绑定的端口号。 -
在网络通信中,端口号用于区分同一台设备上运行的不同服务。
-
-
IP 地址
INADDR_ANY:-
addr.sin_addr.s_addr = INADDR_ANY;表示我们愿意接受来自任何 IP 地址的连接。 -
INADDR_ANY是一个特殊的 IP 地址,它告诉系统绑定到所有可用的网络接口上。
-
-
bind函数:-
bind函数用于将套接字描述符(lfd)绑定到一个特定的地址和端口上。 -
语法:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); -
参数:
-
sockfd:要绑定的套接字描述符。 -
addr:指向包含所需地址信息的缓冲区的指针。在这里,我们将其转换为sockaddr类型的指针,因为bind函数期望的是这种类型。 -
addrlen:addr所指缓冲区的长度。
-
-
-
错误处理:
-
如果
bind函数返回-1,表示绑定操作失败。 -
在这种情况下,代码使用
perror函数打印出错误信息,并返回-1以指示上层调用者发生了错误。
-
总结
上述代码段的作用是将之前创建的套接字描述符 lfd 绑定到一个特定的端口上,并接受来自任何 IP 地址的连接。这是网络服务器程序中常见的步骤之一,用于准备监听来自客户端的连接请求。如果绑定操作失败,代码会打印出错误信息并返回 -1。
// 4. 设置监听
ret = listen(lfd, 128);
if (ret == -1)
{
perror("listen");
return -1;
}
知识点讲解
-
listen函数:-
listen函数用于将一个套接字描述符(在这里是lfd)设置为监听模式,准备接受进来的连接请求。 -
语法:
int listen(int sockfd, int backlog); -
参数:
-
sockfd:要设置为监听模式的套接字描述符。 -
backlog:指定在套接字排队的最大连接请求数。当请求数超过这个值时,新的连接请求将被拒绝。这里的128表示最多可以有 128 个连接请求在排队等待处理。
-
-
-
监听的作用:
-
当一个套接字被设置为监听模式后,它就可以接受来自客户端的连接请求了。
-
在服务器程序中,这通常是在绑定套接字到特定的地址和端口之后进行的下一步操作。
-
-
错误处理:
-
如果
listen函数返回-1,表示设置监听失败。 -
在这种情况下,代码使用
perror函数打印出错误信息,并返回-1以指示上层调用者发生了错误。
-
总结
上述代码段的作用是将套接字描述符 lfd 设置为监听模式,并指定最多可以有 128 个连接请求在排队等待处理。这是网络服务器程序中常见的步骤之一,用于准备接受来自客户端的连接请求。如果设置监听失败,代码会打印出错误信息并返回 -1。
现在,服务器已经准备好接受连接请求了。当有客户端尝试连接到服务器时,服务器可以使用 accept 函数来接受连接,并创建一个新的套接字描述符来处理与客户端的通信。
完整代码:
int initListenFd(unsigned short port)
{
// 1. 创建监听的fd
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if (lfd == -1)
{
perror("socket");
return -1;
}
// 2. 设置端口复用
int opt = 1;
int ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
if (ret == -1)
{
perror("setsockopt");
return -1;
}
// 3. 绑定
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(lfd, (struct sockaddr*)&addr, sizeof addr);
if (ret == -1)
{
perror("bind");
return -1;
}
// 4. 设置监听
ret = listen(lfd, 128);
if (ret == -1)
{
perror("listen");
return -1;
}
// 返回fd
return lfd;
}
更多推荐

所有评论(0)