首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >网络函数bind源码分析

网络函数bind源码分析

原创
作者头像
theanarkh
修改2019-01-10 10:09:48
5330
修改2019-01-10 10:09:48
举报
文章被收录于专栏:原创分享原创分享

static int sock_bind(int fd, struct sockaddr *umyaddr, int addrlen)
{
	struct socket *sock;
	int i;
	char address[MAX_SOCK_ADDR];
	int err;

	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
		return(-EBADF);
	// 通过文件描述符找到对应的socket,见socket函数源码分析的视图
	if (!(sock = sockfd_lookup(fd, NULL))) 
		return(-ENOTSOCK);
  
	if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0)
	  	return err;
  
	if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0) 
	{
		return(i);
	}
	return(0);
}
```
可见bind函数是直接调用底层af_inet.c的inet_bind
```c
// 给socket绑定一个地址
static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
	       int addr_len)
{
	struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
	struct sock *sk=(struct sock *)sock->data, *sk2;
	unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
	int chk_addr_ret;

	/* check this error. */
	if (sk->state != TCP_CLOSE)
		return(-EIO);
	if(addr_len<sizeof(struct sockaddr_in))
		return -EINVAL;
	// raw是链路层,不需要端口	
	if(sock->type != SOCK_RAW)
	{	// 已经绑定了端口
		if (sk->num != 0) 
			return(-EINVAL);
		
		snum = ntohs(addr->sin_port);

		/*
		 * We can't just leave the socket bound wherever it is, it might
		 * be bound to a privileged port. However, since there seems to
		 * be a bug here, we will leave it if the port is not privileged.
		 */
		// 端口无效则随机获取一个非root才能使用的端口
		if (snum == 0) 
		{
			snum = get_new_socknum(sk->prot, 0);
		}
		// 小于1024的端口需要超级用户权限
		if (snum < PROT_SOCK && !suser()) 
			return(-EACCES);
	}
	// 判断ip
	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST)
		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
	  	
	if (chk_addr_ret || addr->sin_addr.s_addr == 0)
		sk->saddr = addr->sin_addr.s_addr;
	
	if(sock->type != SOCK_RAW)
	{
		/* Make sure we are allowed to bind here. */
		cli();
		for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
					sk2 != NULL; sk2 = sk2->next) 
		{
		/* should be below! */
			if (sk2->num != snum) 
				continue;
			// 端口已经被使用,没有设置可重用标记,比如断开了解后在2msl内是否可以重用
			if (!sk->reuse)
			{
				sti();
				return(-EADDRINUSE);
			}
			
			if (sk2->num != snum) 
				continue;		/* more than one */
			if (sk2->saddr != sk->saddr) 
				continue;	/* socket per slot ! -FB */
			// 被监听的端口不能同时被使用
			if (!sk2->reuse || sk2->state==TCP_LISTEN) 
			{
				sti();
				return(-EADDRINUSE);
			}
		}
		sti();
		// 保证该sk不在sock_array队列里
		remove_sock(sk);
		// 挂载到sock_array里
		put_sock(snum, sk);
		// tcp头中的源端口
		sk->dummy_th.source = ntohs(sk->num);
		sk->daddr = 0;
		sk->dummy_th.dest = 0;
	}
	return(0);
}
由代码中可以看到,bind函数主要做的事情是判断一下端口和ip和可用性,然后把sock结构体挂载到协议对应的sock_array哈希列表中。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档