您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何深入理解TCP/IP協議的socket實現,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
socket大家都知道是用于網絡通信的,也知道他是ip和端口的組合。但是很多同學可能不是很清楚socket的原理和實現。下面我們深入理解一下socket到底是什么。
我們回憶一下socket編程的步驟,不管是客戶端還是服務端,第一個調的函數都是socket。我們就從這個函數的實現開始,看看一個socket到底是什么。
// 新建一個socket結構體,并且創建一個下層的sock結構體,互相關聯
static int sock_socket(int family, int type, int protocol)
{
int i, fd;
struct socket *sock;
struct proto_ops *ops;
// 找到對應的協議族,比如unix域、ipv4
for (i = 0; i < NPROTO; ++i)
{ // 從props數組中找到family協議對應的操作函數集,props由系統初始化時sock_register進行操作
if (pops[i] == NULL) continue;
if (pops[i]->family == family)
break;
}
if (i == NPROTO)
{
return -EINVAL;
}
// 函數集
ops = pops[i];
// 檢查一下類型
if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
type != SOCK_SEQPACKET && type != SOCK_RAW &&
type != SOCK_PACKET) || protocol < 0)
return(-EINVAL);
// 分配一個新的socket結構體
if (!(sock = sock_alloc()))
{
...
}
// 設置類型和操作函數集
sock->type = type;
sock->ops = ops;
if ((i = sock->ops->create(sock, protocol)) < 0)
{
sock_release(sock);
return(i);
}
// 返回一個新的文件描述符
if ((fd = get_fd(SOCK_INODE(sock))) < 0)
{
sock_release(sock);
return(-EINVAL);
}
return(fd);
}
我們從上到下,逐步分析這個過程。
1 根據傳的協議類型,找到對應的函數集,因為不同的協議族他的底層操作是不一樣的。
2 分配一個socket結構體。定義如下。我們大概了解一下字段就行。
struct socket {
short type; /* SOCK_STREAM, ... */
socket_state state;
long flags;
struct proto_ops *ops;
// 這個字段要記一下
void *data;
struct socket *conn;
struct socket *iconn;
struct socket *next;
struct wait_queue **wait;
struct inode *inode;
struct fasync_struct *fasync_list;
};
struct socket *sock_alloc(void)
{
struct inode * inode;
struct socket * sock;
// 獲取一個可用的inode節點
inode = get_empty_inode();
if (!inode)
return NULL;
// 初始化某些字段
inode->i_mode = S_IFSOCK;
inode->i_sock = 1;// socket文件
inode->i_uid = current->uid;
inode->i_gid = current->gid;
// 指向inode的socket結構體,初始化inode結構體的socket結構體
sock = &inode->u.socket_i;
sock->state = SS_UNCONNECTED;
sock->flags = 0;
sock->ops = NULL;
sock->data = NULL;
sock->conn = NULL;
sock->iconn = NULL;
sock->next = NULL;
sock->wait = &inode->i_wait;
// 互相引用
sock->inode = inode; /* "backlink": we could use pointer arithmetic instead */
sock->fasync_list = NULL;
// socket數加一
sockets_in_use++;
// 返回新的socket結構體,他掛載在inode中
return sock;
}
sock_alloc首先分配了一個inode,inode節點里有一個socket結構體,然后初始化socket結構體的一些字段,并把他的地址返回。
3 這時候我們拿到一個socket結構體。接著調create函數(省略了部分代碼)。
// 創建一個sock結構體,和socket結構體互相關聯
static int inet_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct proto *prot;
int err;
// 分配一個sock結構體
sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);
switch(sock->type)
{
case SOCK_STREAM:
protocol = IPPROTO_TCP;
// 函數集
prot = &tcp_prot;
break;
case SOCK_DGRAM:
protocol = IPPROTO_UDP;
prot=&udp_prot;
break;
}
// sock結構體的socket字段指向上層的socket結構體
sk->socket = sock;
// 省略一堆對sock結構體的初始化代碼
}
我們發現創建一個socket的時候,申請了一個socket結構體,同時也申請了一個sock結構體。為什么需要兩個結構體,并且這兩個結構體關聯在一起呢?這要說到網絡協議的復雜性,而這個設計就是linux對這個復雜性的解決方案。我們回頭看看socket函數的參數。
socket(int family, int type, int protocol)
family是協議簇,比如unix域、ipv4、ipv6,type是在第一個參數的基礎上的子分類。比如ipv4下有tcp、udp、raw、packet。protocol對tcp、udp沒用,對raw、packet的話是標記上層協議類型。這好比一棵樹一樣,從根節點開始,有很多分支。socket結構體是整個網絡協議實現的最上層結構,是第一層抽象。根據協議簇的不同,有不同的實現函數,在同一協議簇下,也有不同的子分類,比如ipv4下有tcp、udp等。不同子類具體的邏輯也不一樣。即數據結構和算法都不一樣。所以socket結構體有一個data字段,他是自定義的,對于ipv4的實現,他是指向一個sock結構體,對于unix域的實現,unix_proto_data結構體。這就解決了不同協議簇(family)不同實現的問題。那對于同一協議簇下的不同子類型,又如何實現呢?比如ipv4下的tcp、udp。linux給出的方案是在sock結構體中定義一個字段,根據子類型type的值,指向不同的底層協議函數集。
關于如何深入理解TCP/IP協議的socket實現就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。