由于,本人是主修java的,所以以下内容可能不是很精通,各位看完后尽可评论。
以下皆是在linux的描述
第一步,通过socket拿到fd
Socket()函数:创建用于通信的端点并返回描述符。
int fd = socket(AF_INET, SOCK_STREAM, 0);
它的第一个参数便是通信域,这里我举出常用的。
AF_INET是ipv4的。
AF_INET6是ipv6的。
它的第二个参数便是套接字具有指定的类型,指定通信语义
套接字类型与协议族所相关。
常用的套接字类型有:
SOCK_STREAM:提供有序的、可靠的、双向的、基于连接的字节流。一种带外数据传输装置
主义可能得到支持。
SOCK_DGRAM:支持数据报(无连接、最大长度固定的不可靠消息)。
看上去是不是感觉就是,一个是tcp,另外一个是udp呢0.0.
它的第三个参数是协议族的标识符。这里的0表示使用默认的协议族。
该协议指定套接字使用的特定协议。通常只存在一个协议来支持
给定协议族中的特定套接字类型,在这种情况下,可以将protocol指定为0。然而,这是可能的
可能存在许多协议,在这种情况下,必须以这种方式指定特定的协议。
第二步,设置套接字选项
getsockopt, setsockopt 函数: get and set options on sockets
int val = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
fd
:上文拿到的文件描述符。SOL_SOCKET
:这是一个预定义的常量,表示我们要修改的是套接字级别的选项。SO_REUSEADDR
:这是一个预定义的常量,表示我们要修改的是“地址重用”选项。当一个套接字关闭后,操作系统通常会保留该套接字的地址,以便将来重新使用。通过将此选项设置为1,我们可以告诉操作系统在套接字关闭后立即释放其地址,而不是等待一段时间。&val
:这是一个指向整数变量val
的指针,该变量的值被设置为1。这是因为setsockopt()
函数的最后一个参数需要一个指向整数的指针。sizeof(val)
:这是val
变量的大小(以字节为单位)。在这种情况下,它是4,因为int
通常占用4个字节。
第三步进行bind和listen
// bind
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = ntohs(1234);
addr.sin_addr.s_addr = ntohl(0); // wildcard address 0.0.0.0
int rv = bind(fd, (const sockaddr *)&addr, sizeof(addr));
if (rv) {
die("bind()");
}
// listen
rv = listen(fd, SOMAXCONN);
if (rv) {
die("listen()");
}
这个sockaddr_in结构体:
linux上是这个:#include
windows上是这个:#include
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
ntohs()函数的作用是:将无符号短整数netshort从网络字节顺序转换为主机字节顺序。
说白了,addr
结构体的sin_port
字段,表示要绑定的端口号。ntohs
函数将主机字节序的端口号转换为网络字节序。在这里,端口号被设置为1234。
ntohl
函数将主机字节序的IP地址转换为网络字节序。在这里,IP地址被设置为通配符地址0.0.0.0,表示允许任何IP地址连接到该套接字。
int rv = bind(fd, (const sockaddr *)&addr, sizeof(addr));
这行代码调用了bind
函数,将套接字fd
绑定到指定的网络地址和端口上。bind
函数的第一个参数是要绑定的套接字的文件描述符,第二个参数是指向网络地址结构的指针,第三个参数是网络地址结构的大小。在这里,fd
是要绑定的套接字的文件描述符,&addr
是指向网络地址结构的指针,sizeof(addr)
是网络地址结构的大小。
最后,bind
函数返回一个整数值,表示操作的结果。
listen(fd, SOMAXCONN);
是调用 listen()
函数,该函数用于使套接字进入监听状态,等待客户端的连接请求。其中,fd
是之前创建的套接字文件描述符,SOMAXCONN
是一个系统定义的最大连接数。
第四步,循环接受并处理请求
while (true) {
// accept
struct sockaddr_in client_addr = {};
socklen_t socklen = sizeof(client_addr);
int connfd = accept(fd, (struct sockaddr *)&client_addr, &socklen);
if (connfd < 0) {
continue; // error
}
while (true) {
// here the server only serves one client connection at once
int32_t err = one_request(connfd);
if (err) {
break;
}
}
close(connfd);
}