上一篇简单说了服务器端接收,在客户端发起连接之前,服务器只是再监听自己的接收队列,在没有数据时把进程切换出去,等待连接,这篇文章说一下客户端连接
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, argv[1], sizeof(server_addr.sun_path));
ret = connect(sockfd, (struct sockaddr *)&server_addr, addrlen);
客户端需要把想连接的地址写如第二个参数中去, unix socket就是服务器的监听地址
-
SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
-
int, addrlen)
-
{
-
struct socket *sock;
-
struct sockaddr_storage address;
-
int err, fput_needed;
-
sock = sockfd_lookup_light(fd, &err, &fput_needed); // 根据socket描述符找到socket结构
-
if (!sock)
-
goto out;
-
err = move_addr_to_kernel(uservaddr, addrlen, &address); //拷贝服务器地址
-
if (err < 0)
-
goto out_put;
-
-
err =
-
security_socket_connect(sock, (struct sockaddr *)&address, addrlen);
-
if (err)
-
goto out_put;
-
-
err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
-
sock->file->f_flags);
-
out_put:
-
fput_light(sock->file, fput_needed);
-
out:
-
return err;
-
}
unix SOCK_STREAM socket 调用 unix_stream_connect
-
static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
-
int addr_len, int flags)
-
{
-
struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
-
struct sock *sk = sock->sk;
-
struct net *net = sock_net(sk);
-
struct unix_sock *u = unix_sk(sk), *newu, *otheru;
-
struct sock *newsk = NULL;
-
struct sock *other = NULL;
-
struct sk_buff *skb = NULL;
-
unsigned int hash;
-
int st;
-
int err;
-
long timeo;
-
-
err = unix_mkname(sunaddr, addr_len, &hash);
-
if (err < 0)
-
goto out;
-
addr_len = err;
-
-
if (test_bit(SOCK_PASSCRED, &sock->flags) && !u->addr &&
-
(err = unix_autobind(sock)) != 0)
-
goto out;
-
-
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
-
-
/* First of all allocate resources.
-
If we will make it after state is locked,
-
we will have to recheck all again in any case.
-
*/
-
-
err = -ENOMEM;
-
-
/* create new sock for complete connection */
-
newsk = unix_create1(sock_net(sk), NULL);
-
if (newsk == NULL)
-
goto out;
-
-
/* Allocate skb for sending to listening sock */
-
skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
-
if (skb == NULL)
-
goto out;
-
-
restart:
-
/* Find listening sock. */
-
other = unix_find_other(net, sunaddr, addr_len, sk->sk_type, hash, &err); // 查找对端sock
-
if (!other)
-
goto out;
-
-
/* Latch state of peer */
-
unix_state_lock(other);
-
-
/* Apparently VFS overslept socket death. Retry. */
-
if (sock_flag(other, SOCK_DEAD)) {
-
unix_state_unlock(other);
-
sock_put(other);
-
goto restart;
-
}
-
-
err = -ECONNREFUSED;
-
if (other->sk_state != TCP_LISTEN) // 必须是监听状态
-
goto out_unlock;
-
if (other->sk_shutdown & RCV_SHUTDOWN) // 没关闭接收端
-
goto out_unlock;
-
-
if (unix_recvq_full(other)) { //接收队列满了怎么办
-
err = -EAGAIN;
-
if (!timeo)
-
goto out_unlock;
-
-
timeo = unix_wait_for_peer(other, timeo);
-
-
err = sock_intr_errno(timeo);
-
if (signal_pending(current))
-
goto out;
-
sock_put(other);
-
goto restart;
-
}
-
-
/* Latch our state.
-
-
It is tricky place. We need to grab our state lock and cannot
-
drop lock on peer. It is dangerous because deadlock is
-
possible. Connect to self case and simultaneous
-
attempt to connect are eliminated by checking socket
-
state. other is TCP_LISTEN, if sk is TCP_LISTEN we
-
check this before attempt to grab lock.
-
-
Well, and we have to recheck the state after socket locked.
-
*/
-
st = sk->sk_state;
-
-
switch (st) {
-
case TCP_CLOSE: // 我们这边的状态也要正确
-
/* This is ok... continue with connect */
-
break;
-
case TCP_ESTABLISHED:
-
/* Socket is already connected */
-
err = -EISCONN;
-
goto out_unlock;
-
default:
-
err = -EINVAL;
-
goto out_unlock;
-
}
-
-
unix_state_lock_nested(sk);
-
-
if (sk->sk_state != st) {
-
unix_state_unlock(sk);
-
unix_state_unlock(other);
-
sock_put(other);
-
goto restart;
-
}
-
-
err = security_unix_stream_connect(sk, other, newsk);
-
if (err) {
-
unix_state_unlock(sk);
-
goto out_unlock;
-
}
-
-
/* The way is Fastly set all the necessary fields... */
-
-
sock_hold(sk);
-
unix_peer(newsk) = sk; //设置对端地址
-
newsk->sk_state = TCP_ESTABLISHED;
-
newsk->sk_type = sk->sk_type;
-
init_peercred(newsk);
-
newu = unix_sk(newsk);
-
RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
-
otheru = unix_sk(other);
-
-
/* copy address information from listening to new sock*/ //对端的监听地址就是服务器的地址
-
if (otheru->addr) {
-
atomic_inc(&otheru->addr->refcnt);
-
newu->addr = otheru->addr;
-
}
-
if (otheru->path.dentry) {
-
path_get(&otheru->path);
-
newu->path = otheru->path;
-
}
-
-
/* Set credentials */
-
copy_peercred(sk, other);
-
-
sock->state = SS_CONNECTED;
-
sk->sk_state = TCP_ESTABLISHED;
-
sock_hold(newsk);
-
-
smp_mb__after_atomic(); /* sock_hold() does an atomic_inc() */
-
unix_peer(sk) = newsk; // 设置对端地址
-
-
unix_state_unlock(sk);
-
-
/* take ten and and send info to listening sock */
-
spin_lock(&other->sk_receive_queue.lock);
-
__skb_queue_tail(&other->sk_receive_queue, skb); //将生成的skb加入到对端的接收队列中
-
spin_unlock(&other->sk_receive_queue.lock);
-
unix_state_unlock(other);
-
other->sk_data_ready(other); // 通知对端有数据接收
-
sock_put(other);
-
return 0;
-
-
out_unlock:
-
if (other)
-
unix_state_unlock(other);
-
-
out:
-
kfree_skb(skb);
-
if (newsk)
-
unix_release_sock(newsk, 0);
-
if (other)
-
sock_put(other);
-
return err;
-
}
connect实现的功能就是构造一个空的skb,将它发送到服务器的接收队列中,并唤醒阻塞在accept状态的服务端
sk->sk_data_ready = sock_def_readable;
当有数据可读时,内核调用sock_def_readable函数
-
static void sock_def_readable(struct sock *sk)
-
{
-
struct socket_wq *wq;
-
-
rcu_read_lock();
-
wq = rcu_dereference(sk->sk_wq);
-
if (wq_has_sleeper(wq)) //sock 等待队列中有进程
-
wake_up_interruptible_sync_poll(&wq->wait, POLLIN | POLLPRI |
-
POLLRDNORM | POLLRDBAND); //唤醒等待队列上的进程
-
sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); //通知异步队列上的进程
-
rcu_read_unlock();
-
}
阅读(2427) | 评论(0) | 转发(0) |