在套接口中,一个套接字只是用户程序与内核交互信息的枢纽,它自身没有太多的信息,也没有网络协议地址和 端口号等信息,在进行网络通信的时候,必须把一个套接字与一个地址相关联,这个过程就是地址绑定的过程。许多时候内核会我们自动绑定一个地址,然而有时用 户可能需要自己来完成这个绑定的过程,以满足实际应用的需要,最典型的情况是一个服务器进程需要绑定一个众所周知的地址或端口以等待客户来连接。这个事由 bind的函数完成。
从bind函数功能我们很容易推测出这个函数的需要的参数与相应的返回值,如果此时大家已经对socket接口有点熟悉了:
#include<sys/socket.h>
int bind( int sockfd, struct sockaddr* addr, socklen_t addrlen)
返回:0 ──成功, - 1 ──失败
参数sockfd
指定地址与哪个套接字绑定,这是一个由之前的socket函数调用返回的套接字。调用bind的函数之后,该套接字与一个相应的地址关联,发送到这个地址的数据可以通过这个套接字来读取与使用。
参数addr
指定地址。这是一个地址结构,并且是一个已经经过填写的有效的地址结构。调用bind之后这个地址与参数sockfd指定的套接字关联,从而实现上面所说的效果。
参数addrlen
正如大多数socket接口一样,内核不关心地址结构,当它复制或传递地址给驱动的时候,它依据这个值来确定需要复制多少数据。这已经成为socket接口中最常见的参数之一了。
下面来看代码 先初始化地址
//初始化地址
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9700);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
然后将这些参数传入
res = QuicBind(socket, (struct sockaddr*)&addr, sizeof(addr));
//判断返回值是正还是负
CHECK_RES(res, "epoll_ctl");
跟踪一个这个CHECK_RES函数
#define CHECK_RES(res, api) \
do {\
if (res < 0) {\//如果返回值小于0就报错
perror(api " error");\
return 1;\
}\
} while (0)
下面跟踪一下这个QuicBind()函数
int QuicBind(QuicSocket sock, const struct sockaddr* addr, socklen_t addrlen)
{
auto socket = EntryBase::GetFdManager().Get(sock);
if (!socket || socket->Category() != EntryCategory::Socket) {
DebugPrint(dbg_api, "sock = %d, return = -1, errno = EBADF", sock);
errno = EBADF;
return -1;
}
int res = ((QuicSocketEntry*)socket.get())->Bind(addr, addrlen);
DebugPrint(dbg_api, "sock = %d, return = %d, errno = %d", sock, res, errno);
return res;
}
//找到这个Bind(addr, addrlen)函数
int QuicSocketEntry::Bind(const struct sockaddr* addr, socklen_t addrlen)
{
switch (socketState_) {
case QuicSocketState_None:
{
int res = CreateNewUdpSocket();
if (res < 0) return -1;
}
case QuicSocketState_Inited:
{
int res = ::bind(*udpSocket_, addr, addrlen);
if (res == 0)
socketState_ = QuicSocketState_Binded;
return res;
}
default:
errno = EINVAL;
return -1;
}
}
跟进CreateNewUdpSocket()
int QuicSocketEntry::CreateNewUdpSocket()
{
if (udpSocket_) {
errno = EINVAL;
return -1;
}
int fd = ::socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) return -1;
return SetUdpSocket(fd);
}
跟进这个SetUdpSocket(fd)
int QuicSocketEntry::SetUdpSocket(int fd)
{
if (udpSocket_) {
errno = EINVAL;
return -1;
}
int val = ::fcntl(fd, F_GETFL, 0);
if (val == -1) {
::close(fd);
return -1;
}
int res = ::fcntl(fd, F_SETFL, val | O_NONBLOCK);
if (res == -1) {
::close(fd);
return -1;
}
val = GetOpt(sockopt_udp_rmem);
if (!val) val = kDefaultUdpRecvMem;
res = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&val, sizeof(int));
if (res == -1) {
::close(fd);
return -1;
}
val = GetOpt(sockopt_udp_wmem);
if (!val) val = kDefaultUdpWriteMem;
res = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&val, sizeof(int));
if (res == -1) {
::close(fd);
return -1;
}
udpSocket_.reset(new int(fd), [](int* p){
::close(*p);
delete p;
});
socketState_ = QuicSocketState_Inited;
GetConnectionManager().Put(*udpSocket_, impl_->connection_id(), Fd(), true);
return 0;
}