从文件描述符到Socket

https://zhuanlan.zhihu.com/p/215267636

文件描述符

我们知道在linux系统中一切皆文件,而文件又可分为:普通文件、目录文件、链接文件和设备文件。

文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作(包括网络socket操作)的系统调用都通过文件描述符。

在linux系统中开启一个常驻进程 在linux系统中执行

ll /proc/{进程号}/fd
运行结果如下

便可以看到这个进程中的所有文件描述符,细心的话可以看到0 1 2的描述符,一般内核限制进程打开的文件描述符为1024个,这个可以执行ulimit -n 查看。

套接字描述符

套接字描述符是类似于文件描述符。当执行如下代码

server = new ServerSocket(8080);
Socket socket = null;
while (true) {
socket = server.accept();
new Thread(new TimeServerHandler(socket)).start();
}
就会开辟资源,系统分配一块缓冲区 同理执行 ll /proc/{进程号}/fd 可以看到套接字描述符,

图中socket字样的就是套接字描述符也称socket。

什么是socket?

Socket的中文翻译过来就是“套接字”,可以想象当两人打电话接通时,这个通路就是一个socket,包含了两者ip,但是在操作系统来看,光ip并不够,因为一个系统可能运行多个进程,所以就加了个端口。就是两者的端口加ip就能确定这个socket连接从哪来到哪去。其实在真正客户端连接服务端时,客户端会有一个socket套接字描述符,服务端也有一个socket套接字描述符。真正的信息互通需要这两个socket共同工作。我们知道真正的网络通信需要依赖传输层和链路层。

那么传输协议层如TCP/IP与socket套接字什么关系呢?

实际上,Socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。

listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family      = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port        = htons(13);	/* daytime server */
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for ( ; ; ) {
	connfd = Accept(listenfd, (SA *) NULL, NULL);
    ticks = time(NULL);
    snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
    Write(connfd, buff, strlen(buff));
	Close(connfd);
}

以上是c语言代码,取自《unix网络编程》大家只需要记住socket(),bind(),listen(),和accep()这几个系统调用便是用来简化连接建立的api罢了,其中accept()是阻塞的(后续会专门出文章介绍什么是阻塞非阻塞什么事同步异步)。

posted @ 2021-06-15 22:44  某菜狗  阅读(114)  评论(0编辑  收藏  举报