本文共 1899 字,大约阅读时间需要 6 分钟。
一般Linux开发程序员来看,外部的设备都是普通文件,都可以通过读写访问来实现发送和接收数据包。但是网卡有些意外,因为每个层次使用了不同的通信协议,建立连接需要指定许多选项,不能通过打开设备来完成这些任务。后来就有了套接字的特殊结构,具体怎么来的就不说了就是一个美国政府和伯克利分校的项目中诞生的,现在已经成为了工业标准,在POSIX标准中也定义了套接字,当然linux必然实现了套接字了。
使用套接字的时候需要区分地址和协议族(是ipv4还是ipv6),区分是基于流的还是数据报的(是TCP还是UDP)。
本章会对基本的套接字数据结构及使用进行说明介绍。
内核与用户空间套接字之间的接口实现在C标准库中,使用socketcall系统,该函数是一个多路分解器。
socket 位于传输层协议之上,屏蔽了不同网络协议之间的差异。
socket 是网络编程的入口,它提供了大量的系统调用,构成了网络程序的主体
在Linux系统中,socket 属于文件系统的一部分,网络通信可以被看作是对文件的读取,使得我们对网络的控制和对文件的控制一样方便。
定义在文件include/linux/net.h中。
struct socket {
socket_state state;
short type;
unsigned long flags;
struct socket_wq __rcu *wq;
struct file *file;
struct sock *sk;
const struct proto_ops *ops;
};
其中state表示套接字的链接状态,定义在
include/uapi/linux/net.h文件中
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;
其中proto_ops指针变量指向一个数据结构,定义在文件:
include/linux/net.h.结构中包含的sock指针,指向sock结构。
关于socketcall系统调用,可以在
文章中查看,有描述到该系统调用。该系统调用是网络相关系统调用的入口。
使用recvfrom和recv以及文件相关的readv和read函数来接收数据。以recvfrom为例,如果调用recvfrom内核中的代码函数是sys_recvfrom。
定义在net/socket.c 文件中。
SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
unsigned int, flags, struct sockaddr __user *, addr,
int __user *, addr_len)
函数会先调用sockfd_lookup_light函数,从查找对应的sock.没找到则退出。
然后调用sock_recvmsg(特定于协议,tcp使用tcp_recvmsg,udp使用udp_recvmsg),从队列中获取分组,如果没有就阻塞。
最后调用move_addr_to_user函数,将数据从内核移动到用户空间。
发送该数据可以使用两个与网络有关的库函数(sendto和send)或文件层的write和writev函数。
以sys_sendto为例,定义如下:
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
unsigned int, flags, struct sockaddr __user *, addr,
int, addr_len)
先调用 sockfd_lookup_light 函数获得 sock ,如果不存在则退出接着调用 move_addr_to_kernel 函数,将数据从用户层复制到内核。最后调用(协议相关的) sock_sendmsg 函数来发送。转载地址:http://gjjpx.baihongyu.com/