套接字Socket编程(下)

简介: Socket,原意插座、插口。写软件程序时,可以想象成一根网线,一头插在客户端,一头插在服务端,然后进行通信。所以通信前,双方都要建立一个Socket。

服务器如何接更多的项目?

建立连接后,进行一个while循环:

  • 客户端发了收
  • 服务端收了发

这只是网络编程第一步,使用这种方法,只能一对一沟通。

若你是个服务器,同时只能服务一个客户,那肯定不行。那我肯定能接的服务越多越好。

那理论值是多少呢?即最大连接数,系统会用一个四元组来标识一个TCP连接。

{本机IP, 本机端口, 对端IP, 对端端口}

服务器通常固定在某个本地端口上监听,等待客户端的连接请求。

因此,服务端的TCP连接四元组只有对端IP,即客户端的IP和对端端口,也即客户端的端口是可变的,因此:

最大TCP连接数=客户端IP数 × 客户端端口数

比如最常用的IPv4:

  • 客户端的IP数,max=2^32
  • 客户端的端口数,max=2^16


即服务端单机最大TCP连接数,约为2^48。

服务端最大并发TCP连接数远不能达到理论上限:


  • fd限制
    Socket都是文件,所以要通过ulimit配置fd的数目
  • 内存
    按上面的数据结构,每个TCP连接都要占用一定内存,os有限。
    所以,在资源有限情况下,要想服务更多客户,就得降低每个客户消耗的资源数。
    那有哪些方案呢?

多进程

将项目外包给其他公司,相当于你是个代理,在那里监听请求。一旦建立了一个连接,就会有一个已连接Socket,这时你可以创建一个子进程,然后将基于已连接Socket的交互交给这个新的子进程来做。

就像来了个新项目,但项目不一定你做,可以再注册一家子公司,招点人,然后把项目转包给这家子公司做,以后对接就交给这家子公司,你就可以专注接新的项目了。


问题是你如何创建子公司,又怎么将项目交给子公司?


Linux使用fork创建子进程,基于父进程完全拷贝一个子进程。在Linux内核中,会复制fd的列表,也会复制内存空间,还会复制一条记录当前执行到了哪行程序的进程。

显然,复制的时候在调用fork,复制完后,父进程、子进程都会记录当前刚刚执行完的fork。

这两个进程刚复制完时,基本一样,只是根据fork返回值区分:

  • 返回值是0,则是子进程
  • 返回值是其它整数,就是父进程

进程复制过程

image.png

因为复制了fd列表,而fd都是指向整个内核统一的打开文件列表的,因而父进程刚才因为accept创建的已连接Socket也是一个文件描述符,同样也会被子进程获得。


接下来,子进程就可以通过这个已连接Socket和客户端进行互通了,当通信完毕之后,就可以退出进程,那父进程如何知道子进程干完了项目,要退出呢?还记得fork返回的时候,如果是整数就是父进程吗?这个整数就是子进程的ID,父进程可以通过这个ID查看子进程是否完成项目,是否需要退出。

多线程

将项目转包给独立的项目组,之前的方案若每次接个项目,都申请一个新公司,然后干完了,就注销掉这个公司,实在太消耗精力。毕竟一个新公司要有新公司的资产,有新的办公家具,每次都买了再卖,不划算。


考虑使用线程,相比于进程,更轻量级。

  • 创建进程相当于成立新公司,购买新办公家具
  • 创建线程,就相当于在同一个公司成立项目组。一个项目做完了,那这个项目组就可以解散,组成另外的项目组,办公家具还可复用。


Linux通过pthread_create创建一个线程,也调用do_fork。

虽然新线程在task列表会新创建一项,但很多资源,例如fd列表、进程空间,还是共享的,只不过多了一个引用。


新的线程也可以通过已连接Socket处理请求,达到并发处理的目的。


基于进程或线程模型其实还有问题。新到来一个TCP连接,就需要分配一个进程或者线程。一台机器无法创建很多进程或者线程。

C10K,一台机器要维护1万个连接,就要创建1万个进程或线程吗,那操作系统无法承受。如果维持1亿用户在线需要10万台服务器,成本也太高了。


C10K问题就是你接项目接的太多了,如果每个项目都成立单独的项目组,就要招聘10万人,你肯定养不起,那怎么办呢?

IO多路复用,一个线程维护多个Socket

一个项目组支撑多个项目,这时每个项目组都应该有个项目进度墙,将自己组看的项目列在那里,然后每天通过项目墙看每个项目的进度,一旦某个项目有了进展,就派人去盯一下。


由于Socket是文件描述符,因而某个线程盯的所有的Socket,都放在一个文件描述符集合fd_set中,这就是项目进度墙,然后调用select函数来监听文件描述符集合是否有变化。

一旦有变化,就会依次查看每个文件描述符。那些发生变化的文件描述符在fd_set对应的位都设为1,表示Socket可读或者可写,从而可以进行读写操作,然后再调用select,接着盯着下一轮的变化。

IO多路复用,从“派人盯着”到“有事通知”

一个项目组支撑多个项目,上面select函数还是有问题,因为每次Socket所在的文件描述符集合中有Socket发生变化的时候,都需要通过轮询,需要将全部项目都过一遍,这大大影响了一个项目组能够支撑的最大的项目数量。因而使用select,能够同时盯的项目数量由FD_SETSIZE限制。


改成事件通知的方式,就会好很多,项目组不需要通过轮询挨个盯着这些项目,而是当项目进度发生变化的时候,主动通知项目组,然后项目组再根据项目进展情况做相应的操作。


能完成这件事情的函数叫epoll,它在内核中的实现不是通过轮询的方式,而是通过注册callback函数的方式,当某个文件描述符发送变化的时候,就会主动通知。


如图所示,假设进程打开了Socket m, n, x等多个文件描述符,现在需要通过epoll来监听是否这些Socket都有事件发生。其中epoll_create创建一个epoll对象,也是一个文件,也对应一个文件描述符,同样也对应着打开文件列表中的一项。在这项里面有一个红黑树,在红黑树里,要保存这个epoll要监听的所有Socket。


当epoll_ctl添加一个Socket的时候,其实是加入这个红黑树,同时红黑树里面的节点指向一个结构,将这个结构挂在被监听的Socket的事件列表中。当一个Socket来了一个事件的时候,可以从这个列表中得到epoll对象,并调用call back通知它。


这种通知方式使得监听的Socket数据增加的时候,效率不会大幅度降低,能够同时监听的Socket的数目也非常的多了。上限就为系统定义的、进程打开的最大文件描述符个数。因而,epoll被称为解决C10K问题的利器。

总结

写一个能够支撑大量连接的高并发的服务端不容易,需要多进程、多线程,而epoll机制能解决C10K问题。

目录
相关文章
|
27天前
|
网络协议 Java
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
这篇文章全面讲解了基于Socket的TCP网络编程,包括Socket基本概念、TCP编程步骤、客户端和服务端的通信过程,并通过具体代码示例展示了客户端与服务端之间的数据通信。同时,还提供了多个案例分析,如客户端发送信息给服务端、客户端发送文件给服务端以及服务端保存文件并返回确认信息给客户端的场景。
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
|
2月前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
27 4
|
2月前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
【7月更文挑战第26天】在网络的数字宇宙中,Python Socket编程是开启网络世界大门的钥匙。本指南将引领你从基础到实战,成为网络世界的建筑师。
49 2
|
2月前
|
网络协议 程序员 视频直播
|
2月前
|
开发者 Python
Python Socket编程:不只是基础,更有进阶秘籍,让你的网络应用飞起来!
【7月更文挑战第25天】在网络应用蓬勃发展的数字时代,Python凭借其简洁的语法和强大的库支持成为开发高效应用的首选。本文通过实时聊天室案例,介绍了Python Socket编程的基础与进阶技巧,包括服务器与客户端的建立、数据交换等基础篇内容,以及使用多线程和异步IO提升性能的进阶篇。基础示例展示了服务器端监听连接请求、接收转发消息,客户端连接服务器并收发消息的过程。进阶部分讨论了如何利用Python的`threading`模块和`asyncio`库来处理多客户端连接,提高应用的并发处理能力和响应速度。掌握这些技能,能使开发者在网络编程领域更加游刃有余,构建出高性能的应用程序。
22 3
|
2月前
|
消息中间件 网络协议 网络安全
Python Socket编程:打造你的专属网络通道,基础篇与进阶篇一网打尽!
【7月更文挑战第26天】在网络编程领域,Python以简洁语法和强大库支持成为构建应用的首选。Socket编程为核心,实现计算机间的数据交换。
51 1
|
2月前
|
安全 网络协议 网络安全
Python Socket编程大揭秘:从菜鸟到黑客的进阶之路,你准备好了吗?
【7月更文挑战第27天】Python Socket编程是网络开发的关键技能,它开启从简单数据传输到复杂应用的大门。Socket作为网络通信的基础,通过Python的`socket`模块可轻松实现跨网通信。
35 0
|
2月前
|
网络协议 Python
告别网络编程迷雾!Python Socket编程基础与实战,让你秒变网络达人!
【7月更文挑战第27天】在网络编程的广阔天地中,Socket编程常被视为一道难关。但用Python这把钥匙,我们可以轻松入门。Socket作为网络通信的基石,在Python中通过`socket`模块封装了底层细节,简化了开发过程。以下是一个基本的TCP服务器与客户端的示例,展示了如何建立连接、收发数据及关闭连接。为了应对实际场景中的并发需求,我们还可以借助多线程技术来提升服务器处理能力。掌握了这些基础知识后,你将逐步揭开网络编程的神秘面纱,踏上编程高手之路!
29 0
|
2月前
|
网络协议 开发者 Python
颠覆传统!Python Socket编程新思维,基础与进阶并重,打造卓越网络能力!
【7月更文挑战第25天】在数字时代,网络通信至关重要,Python的Socket编程简化了这一复杂领域,使初学者也能轻松上手。通过Python的`socket`模块,我们能快速搭建服务器与客户端,实现数据交换。示例代码展示了如何创建、绑定及监听Socket,以及收发消息。掌握基础后,可利用asyncio库探索异步编程,提升通信效率,处理多连接。Python的Socket编程,结合传统与现代技术,助力开发者在网络通信领域取得非凡成就。
42 0
|
2月前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
36 0