socket函数
它用于创建一个新的套接字(socket)。
函数原型
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数解释
domain
:它指定了通信所使用的协议族,常见的取值如下:
AF_INET
:代表 IPv4 协议。
AF_INET6
:代表 IPv6 协议。
AF_UNIX
或AF_LOCAL
:用于本地(Unix 域)套接字通信。
type
:它指定了套接字的类型,常用的类型有:
SOCK_STREAM
:表示面向连接的、可靠的 TCP 套接字。
SOCK_DGRAM
:表示无连接的、不可靠的 UDP 套接字。
SOCK_RAW
:允许程序直接访问底层协议,如 IP、ICMP 等。
protocol
:通常设置为 0,表示让系统根据domain
和type
自动选择合适的协议。
返回值
若函数调用成功,会返回一个新的套接字描述符(非负整数),后续的网络操作会用到这个描述符。
若调用失败,会返回 -1,并设置
errno
来指示具体的错误原因。
示例
#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
using namespace std;
int main()
{
// 初始化网络
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd < 0)
{
/*可能情况
*1、没有连接网络
*2、网卡坏了
*/
perror("socket error");
}
else
{
cout << "服务器网络初始化 socketfd=" << socketfd << endl;
}
return 0;
}
结果
bind函数
用于将一个套接字(socket)与特定的网络地址和端口号绑定在一起。
函数原型
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数解释
sockfd
:这是由socket
函数返回的套接字描述符,代表要进行绑定操作的套接字。
addr
:指向一个sockaddr
类型的结构体指针,该结构体包含了要绑定的地址和端口信息。不过在实际使用中,通常会使用具体的地址结构体,如struct sockaddr_in
(用于 IPv4)或struct sockaddr_in6
(用于 IPv6),然后进行强制类型转换。
addrlen
:表示addr
所指向的结构体的长度。补(struct sockaddr_in):
#include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; // 地址族,通常是 AF_INET(IPv4) in_port_t sin_port; // 端口号,使用网络字节序(大端序)(服务器系统默认IP地址:INADDR_ANY) struct in_addr sin_addr; // IPv4 地址,使用网络字节序 unsigned char sin_zero[8]; // 填充字段,使其大小与 struct sockaddr 相同,一般设置为全 0 };
返回值
若绑定成功,返回 0。
若失败,返回 -1,并设置
errno
来指示具体的错误原因。
listen函数
主要用于将一个套接字(socket)设置为监听状态,以便接收客户端的连接请求。
函数原型
#include <sys/socket.h>
int listen(int sockfd, int backlog);
参数解释
sockfd
:这是一个由socket
函数返回的套接字描述符,代表要设置为监听状态的套接字。该套接字必须已经通过bind
函数绑定到了一个特定的地址和端口。
backlog
:指定了允许在队列中等待处理的最大连接请求数量。当有多个客户端同时发起连接请求时,服务器无法立即处理所有请求,这些请求会被放入一个队列中等待处理。backlog
的值决定了这个队列的最大长度。不同系统对backlog
的最大值有不同的限制,一般来说,常见的值可以设置为 5 或 10。
返回值
若函数调用成功,返回 0。
若调用失败,返回 -1,并设置
errno
来指示具体的错误原因。
accept函数
主要用于服务器端,它会让服务器处于阻塞状态,等待客户端的连接请求。一旦接收到客户端的连接请求,accept
函数就会返回一个新的套接字描述符,这个描述符用于和客户端进行数据通信。而原来的套接字描述符依旧负责监听新的连接请求。
函数原型
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数解释
sockfd
:这是一个已经处于监听状态的套接字描述符,它是通过socket()
创建,再由bind()
绑定地址和端口,最后使用listen()
开始监听的套接字。
addr(前面设置过的化一般为NULL)
:这是一个指向sockaddr
结构体的指针,该结构体用于存储客户端的地址信息。在实际使用时,通常会使用sockaddr_in
(用于 IPv4)或sockaddr_in6
(用于 IPv6)结构体,然后进行类型转换。
addrlen(前面设置过的化一般为NULL)
:这是一个指向socklen_t
类型的指针,它表示addr
所指向的结构体的长度。在调用accept
之前,需要将其初始化为addr
结构体的大小;调用完成后,该指针指向的值会被更新为实际存储的客户端地址信息的长度。
返回值
若调用成功,
accept
会返回一个新的套接字描述符,此描述符用于和客户端进行数据通信。原来的sockfd
仍然用于监听新的连接请求。若调用失败,会返回 -1。
整合示例
#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
using namespace std;
int main()
{
struct sockaddr_in addr;
int length = 0;
int acceptfd = 0;
// 初始化网络
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd < 0)
{
/*可能情况
*1、没有连接网络
*2、网卡坏了
*/
perror("socket error");
return 0;
}
else
{
addr.sin_family = AF_INET;
// 服务器系统默认IP地址
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(10001);
length = sizeof(addr);
// 绑定端口号
if (bind(socketfd, (struct sockaddr*)(&addr), length) == -1)
{
perror("bind error");
return 0;
}
// 监听这个文件描述符,是否有客户端来连接
if (listen(socketfd, 10) == -1)
{
perror("lister error");
return 0;
}
cout << "服务器网络搭建成功" << endl;
// 因为服务器24h长时间开机
while (1)
{
// acceptfd代表已经连接成功的客户端
// 阻塞式函数(等待客户端的到来)
acceptfd = accept(socketfd, NULL, NULL);
cout << "客户端上线,acceptfd=" << acceptfd << endl;
}
}
return 0;
}