http://www.cnblogs.com/hustcat/archive/2009/09/17/1568738.html
http://www.cnblogs.com/hustcat/archive/2009/09/17/1568765.html
Linux网络协议栈(一)——Socket入门(1)
为了实现各种网络的互连,国际标准化组织(ISO)制定了开放式系统互连(OSI)参考模型。尽管OSI的体系结构从理论上讲是比较完整的,但实际上,完全符合OSI各层协议的商用产品却很少进入市场。而使用TCP/IP 协议的产品却大量涌入市场,几乎所有的工作站都配有TCP/IP协议,使得TCP/IP 成为计算机网络的实际的国际标准。
2、套接字(socket)
socket是操作系统的重要组成部分之一,它是网络应用程序的基础。从层次上来说,它位于应用层,是操作系统为应用程序员提供的API,通过它,应用程序可以访问传输层协议。
1、socket 位于传输层协议之上,屏蔽了不同网络协议之间的差异;
2、socket是网络编程的入口,它提供了大量的系统调用,构成了网络程序的主体;
3、在Linux系统中,socket属于文件系统的一部分,网络通信可以被看作是对文件的读取,使得我们对网络的控制和对文件的控制一样方便。
2.1、套接字地址
在传输层上,通信端点可由Internet上3个参数描述:所用的协议、IP地址和端口号。这些内容由sockaddr描述:
typedef unsigned short sa_family_t;
// 通用socket地址
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx,协议簇 */
char sa_data[ 14 ]; /* 14 bytes of protocol address */
};
// usr/include/netinet/in.h
// INET地址簇的socket地址
struct in_addr {
__u32 s_addr;
};
struct sockaddr_in {
sa_family_t sin_family; /* Address family: AF_INET */
unsigned short int sin_port; /* Port number,端口 */
struct in_addr sin_addr; /* Internet address,IP地址 */
/* Pad to size of 'struct sockaddr' . */
unsigned char sin_zero[ sizeof ( struct sockaddr) -
sizeof (sa_family_t) -
sizeof (uint16_t) -
sizeof ( struct in_addr)];
};
套接字地址族 | 描述 |
UNIX | UNIX 域套接字 |
INET | 通过 TCP/IP 协议支持的 Internet 地址族 |
AX25 | Amater radio X25 |
APPLETALK | Appletalk DDP |
IPX | Novell IPX |
X25 | X25 |
BSD 套接字类型 | 描述 |
流(stream) | 这种套接字提供了可靠的双向顺序数据流,可保证数据不会在传输过程中丢失、破坏或重复出现。流套接字通过 INET地址族的 TCP 协议实现。 |
数据报(datagram) | 这种套接字也提供双向的数据传输,但是并不对数据的传输提供担保,也就是说,数据可能会以错误的顺序传递,甚至丢失或破坏。这种类型的套接字通过 INET 地址族的 UDP协议实现。 |
原始(raw) | 利用这种类型的套接字,进程可以直接访问底层协议(因此称为原始)。例如,可在某个以太网设备上打开原始套接字,然后获取原始的 IP 数据传输信息。 |
可靠发送的消息 | 和数据报套接字类似,但保证数据被正确传输到目的端。 |
顺序数据包 | 和流套接字类似,但数据包大小是固定的。 |
数据包(packet) | 这并不是标准的 BSD 套接字类型,它是 Linux 专有的BSD 套接字扩展,可允许进程直接在设备级访问数据包。 |
套接字(更确切的说是BSD套接字)为应用程序提供了基本的API,这些API是编写网络应用程序的基础。
3、套接字的实现
套接字最先是在UNIX的BSD版本实现的,所以也叫做BSD套接字,它隐藏了各个协议之间的差异,并向上提供统一的接口。Linux中实现套接字的基本结构:
3.1、BSD套接字
3.1.1、核心数据结构
为了实现BSD套接字,内核提供一个重要的数据结构struct socket,它的定义如下:
struct socket {
socket_state state; // 套接字状态
unsigned long flags;
struct proto_ops * ops; // 操作函数集
struct fasync_struct * fasync_list;
struct file * file; // 每个BSD套接字都有一个inode结点,通过文件对象与其关联起来
struct sock * sk; // socket内部结构,与具体的协议簇(比如PF_INET)相关
wait_queue_head_t wait;
short type; // 套接字类型:如SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, and SOCK_PACKET
unsigned char passcred;
};
// BSD套接字操作函数集
struct proto_ops {
int family;
struct module * owner;
int ( * release) ( struct socket * sock);
int ( * bind) ( struct socket * sock,
struct sockaddr * myaddr,
int sockaddr_len);
int ( * connect) ( struct socket * sock,
struct sockaddr * vaddr,
int sockaddr_len, int flags);
int ( * socketpair)( struct socket * sock1,
struct socket * sock2);
int ( * accept) ( struct socket * sock,
struct socket * newsock, int flags);
int ( * getname) ( struct socket * sock,
struct sockaddr * addr,
int * sockaddr_len, int peer);
unsigned int ( * poll) ( struct file * file, struct socket * sock,
struct poll_table_struct * wait);
int ( * ioctl) ( struct socket * sock, unsigned int cmd,
unsigned long arg);
int ( * listen) ( struct socket * sock, int len);
int ( * shutdown) ( struct socket * sock, int flags);
int ( * setsockopt)( struct socket * sock, int level,
int optname, char __user * optval, int optlen);
int ( * getsockopt)( struct socket * sock, int level,
int optname, char __user * optval, int __user * optlen);
int ( * sendmsg) ( struct kiocb * iocb, struct socket * sock,
struct msghdr * m, size_t total_len);
int ( * recvmsg) ( struct kiocb * iocb, struct socket * sock,
struct msghdr * m, size_t total_len,
int flags);
int ( * mmap) ( struct file * file, struct socket * sock,
struct vm_area_struct * vma);
ssize_t ( * sendpage) ( struct socket * sock, struct page * page,
int offset, size_t size, int flags);
};
// BSD套接字状态
typedef enum {
SS_FREE = 0 , /* not allocated */
SS_UNCONNECTED, /* unconnected to any socket */
SS_CONNECTING, /* in process of connecting */
SS_CONNECTED, /* connected to socket */
SS_DISCONNECTING /* in process of disconnecting */
} socket_state;
// BSD套接字的初始化
void __init sock_init( void )
{
int i;
/*
* Initialize all address (protocol) families.
*/
for (i = 0 ; i < NPROTO; i ++ )
net_families[i] = NULL; // 协议簇数组初始化
/*
* Initialize sock SLAB cache.
*/
// 分配sock缓存
sk_init();
#ifdef SLAB_SKB
/*
* Initialize skbuff SLAB cache
*/
skb_init();
#endif
/*
* Initialize the protocols module.
*/
init_inodecache();
// 注册sockfs文件系统
register_filesystem( & sock_fs_type);
// 安装sockfs
sock_mnt = kern_mount( & sock_fs_type);
/* The real protocol initialization is performed when
* do_initcalls is run.
*/
#ifdef CONFIG_NETFILTER
netfilter_init();
#endif
}
// net/socket.c
// sockfs文件系统的安装点
static struct vfsmount * sock_mnt;
// sockfs文件系统类型
static struct file_system_type sock_fs_type = {
.name = " sockfs " ,
.get_sb = sockfs_get_sb,
.kill_sb = kill_anon_super,
};
// 地址簇及协议信息
static struct net_proto_family * net_families[NPROTO];
3.1.3、BSD套接字的系统调用
实际上,Linux内核只提供了一个与套接字相关的系统调用,即sys_socketcall,应用程序的所有套接字调用都会映射到这个系统调用上。
asmlinkage long sys_socketcall( int call, unsigned long __user * args)
{
unsigned long a[ 6 ];
unsigned long a0,a1;
int err;
if (call < 1 || call > SYS_RECVMSG)
return - EINVAL;
/* copy_from_user should be SMP safe. */
if (copy_from_user(a, args, nargs[call])) // 从用户区拷贝参数
return - EFAULT;
a0 = a[ 0 ];
a1 = a[ 1 ];
switch (call) // 调用相应的函数
{
case SYS_SOCKET:
err = sys_socket(a0,a1,a[ 2 ]);
break ;
case SYS_BIND:
err = sys_bind(a0,( struct sockaddr __user * )a1, a[ 2 ]);
break ;
case SYS_CONNECT:
err = sys_connect(a0, ( struct sockaddr __user * )a1, a[ 2 ]);
break ;
case SYS_LISTEN:
err = sys_listen(a0,a1);
break ;
case SYS_ACCEPT:
err = sys_accept(a0,( struct sockaddr __user * )a1, ( int __user * )a[ 2 ]);
break ;
case SYS_GETSOCKNAME:
err = sys_getsockname(a0,( struct sockaddr __user * )a1, ( int __user * )a[ 2 ]);
break ;
case SYS_GETPEERNAME:
err = sys_getpeername(a0, ( struct sockaddr __user * )a1, ( int __user * )a[ 2 ]);
break ;
case SYS_SOCKETPAIR:
err = sys_socketpair(a0,a1, a[ 2 ], ( int __user * )a[ 3 ]);
break ;
case SYS_SEND:
err = sys_send(a0, ( void __user * )a1, a[ 2 ], a[ 3 ]);
break ;
case SYS_SENDTO:
err = sys_sendto(a0,( void __user * )a1, a[ 2 ], a[ 3 ],
( struct sockaddr __user * )a[ 4 ], a[ 5 ]);
break ;
case SYS_RECV:
err = sys_recv(a0, ( void __user * )a1, a[ 2 ], a[ 3 ]);
break ;
case SYS_RECVFROM:
err = sys_recvfrom(a0, ( void __user * )a1, a[ 2 ], a[ 3 ],
( struct sockaddr __user * )a[ 4 ], ( int __user * )a[ 5 ]);
break ;
case SYS_SHUTDOWN:
err = sys_shutdown(a0,a1);
break ;
case SYS_SETSOCKOPT:
err = sys_setsockopt(a0, a1, a[ 2 ], ( char __user * )a[ 3 ], a[ 4 ]);
break ;
case SYS_GETSOCKOPT:
err = sys_getsockopt(a0, a1, a[ 2 ], ( char __user * )a[ 3 ], ( int __user * )a[ 4 ]);
break ;
case SYS_SENDMSG:
err = sys_sendmsg(a0, ( struct msghdr __user * ) a1, a[ 2 ]);
break ;
case SYS_RECVMSG:
err = sys_recvmsg(a0, ( struct msghdr __user * ) a1, a[ 2 ]);
break ;
default :
err = - EINVAL;
break ;
}
return err;
}
// include/asm/unistd.h
#define __NR_socketcall 102 // 系统调用号
INET套接字就是支持 Internet 地址族的套接字,它位于TCP协议之上, BSD套接字之下,如下:
3.2.1、数据结构