天天看點

核心中的UDP socket流程(7)——udp_sendmsg

作者:[email protected]

sock_sendmsg的代碼很簡單

int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)

{

     struct kiocb iocb;

     struct sock_iocb siocb;

     int ret;

     init_sync_kiocb(&iocb, NULL);

     iocb.private = &siocb;

     ret = __sock_sendmsg(&iocb, sock, msg, size);

     if (-EIOCBQUEUED == ret)

          ret = wait_on_sync_kiocb(&iocb);

     return ret;

}

首先定義了一個struct kiocb類型的iocb——linux核心中所有I/O操作都要依賴于合格結構,然後初始化它。然後調用__sock_sendmsg,而__sock_sendmsg又調用UDP的sendmsg去做真正的發送。

也就是說,對于UDP的socket來說,sendto調用,真正去做工作的是udp_sendmsg這個函數。

int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,

          size_t len)

     struct inet_sock *inet = inet_sk(sk);

     struct udp_sock *up = udp_sk(sk);

     int ulen = len;

     struct ipcm_cookie ipc;

     struct rtable *rt = NULL;

     int free = 0;

     int connected = 0;

     __be32 daddr, faddr, saddr;

     __be16 dport;

     u8 tos;

     int err, is_udplite = IS_UDPLITE(sk);

     int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;

     int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);

将sk轉為udp内部使用的類型指針,在TCP/IP中充斥了這樣的轉換。主要原因是因為對于上層來說,需要一個統一的類型,而到了底層的具體實作時,都會将上層抽象的資料類型,轉為自己所需的類型。

     if (len > 0xFFFF)

          return -EMSGSIZE;

     /*

     * Check the flags.

     */

     if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */

          return -EOPNOTSUPP;

進行一些錯誤檢測。

     ipc.opt = NULL;

     ipc.shtx.flags = 0;

     if (up->pending) {

          /*

          * There are pending frames.

          * The socket lock must be held while it's corked.

          */

          lock_sock(sk);

          if (likely(up->pending)) {

               if (unlikely(up->pending != AF_INET)) {

                    release_sock(sk);

                    return -EINVAL;

               }

               goto do_append_data;

          }

          release_sock(sk);

     }

     ulen += sizeof(struct udphdr);

如果該socket有pending的frame,那麼直接将資料追加。如果沒有就ulen加上udp首部的長度。

     /*

     * Get and verify the address.

     if (msg->msg_name) {

          struct sockaddr_in * usin = (struct sockaddr_in *)msg->msg_name;

          if (msg->msg_namelen sizeof(*usin))

               return -EINVAL;

          if (usin->sin_family != AF_INET) {

               if (usin->sin_family != AF_UNSPEC)

                    return -EAFNOSUPPORT;

          daddr = usin->sin_addr.s_addr;

          dport = usin->sin_port;

          if (dport == 0)

     } else {

          if (sk->sk_state != TCP_ESTABLISHED)

               return -EDESTADDRREQ;

          daddr = inet->inet_daddr;

          dport = inet->inet_dport;

          /* Open fast path for connected socket.

             Route will not be used, if at least one option is set.

          connected = 1;

     ipc.addr = inet->inet_saddr;

如果msg->msg_name不為空,就說明指定了目的位址,對其進行檢驗。如果為空,就就需要對sock的狀态進行檢驗,檢視其是否是連接配接狀态——UDP的socket同樣是可以調用connect,這樣就不需要每次都指定發送位址了。

今天有點困了,就這樣了。

繼續閱讀