网络编程socket(上)(一)

本文涉及的产品
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 1个月
简介: 网络编程socket(上)

一、预备知识

1.1 端口号

socket通信本质


通过IP地址和MAC地址能够将数据发送到对端主机了,但实际上是想将数据发送给对端主机上的某个服务进程,此外,数据的发送者也不是主机,而是主机上的某个进程,比如当使用浏览器访问时,实际就是浏览器进程向对端服务进程发起的请求


aff2a853f9a94722a71b7ccd1323e490.png


socket通信本质上就是两个进程之间在进行通信,只不过这里是跨网络的进程间通信。比如逛淘宝和刷抖音的动作,实际就是手机上的淘宝客户端进程和抖音客户端进程在和对端服务器主机上的淘宝服务进程和抖音服务进程之间进行通信


进程间通信的方式除了管道、消息队列、信号量、共享内存等方式外,还有套接字,只不过前者是不跨网络的,而后者即可以跨网络也可以不跨网络


端口号


两台主机上可能会同时存在多个正在进行跨网络通信的进程,因此当数据到达对端主机后,必须要通过某种方法找到该主机上对应的服务进程,然后将数据交给该进程处理。而当该进程处理完数据后还要对发送端进行响应,因此对端主机也需要知道是发送端上的哪一个进程发送的数据请求


而端口号的作用正是标识一台主机上的某一个进程:


端口号是传输层协议的内容

端口号是一个2字节16位的整数

端口号用来标识一个进程,使得操作系统知道当前数据要交给哪一个进程处理

一个端口号只能被一个进程占用

当数据在传输层进行封装时,就会添加上对应源端口号和目的端口号的信息。这时通过源IP地址+源端口号就能够在网络上唯一标识发送数据的进程,通过目的IP地址+目的端口号就能够在网络上唯一标识接收数据的进程,此时就实现了跨网络的进程间通信


注意: 因为端口号是隶属于某台主机的,所以端口号可以在两台不同的主机当中重复,但是在同一台主机上进行网络通信的进程的端口号不能重复。此外,一个进程可以绑定多个端口号,但是一个端口号不能被多个进程同时绑定


prot VS PID


端口号(port)可唯一标识一台主机上的某个进程,进程ID(PID)的作用也是唯一标识一台主机上的某个进程,那在进行网络通信时为什么不直接用PID来代替port呢?


进程ID(PID)是用来标识系统内所有进程的唯一性的,属于系统级的概念;端口号(port)是用来标识需要对外进行网络数据请求的进程的唯一性的,属于网络的概念


虽然可以使用PID来标识网络进程的唯一性,但会使得系统部分与网络部分交错,导致耦合度较高


并且一个进程可以绑定多个端口号,但一个进程只能对应一个PID


如何通过port找到对应的进程?


实际底层采用哈希的方式建立了端口号和进程PID或PCB之间的映射关系,当底层拿到端口号时就可以直接执行对应的哈希算法,自然能找到该端口号对应的进程


1.2 初步认识TCP协议与UDP协议

网络协议栈贯穿整个体系结构,在应用层、操作系统层和驱动层都有存在。使用系统调用实现网络通信时,不得不面对的协议层就是传输层,而传输层最典型的两种协议就是TCP协议和UDP协议


TCP协议


TCP协议被称为传输控制协议(Transmission Control Protocol),TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议


TCP协议是面向连接的,若两台主机之间想要进行数据传输,那么必须先建立连接,连接建立成功后才能进行数据传输。其次,TCP协议是保证可靠的协议,数据在传输过程中若出现了丢包、乱序等情况,TCP协议都有对应的解决方案


UDP协议


UDP协议被称为用户数据报协议(User Datagram Protocol),UDP协议是一种无需建立连接的、不可靠的、面向数据报的传输层通信协议


使用UDP协议进行通信时无需建立连接,若两台主机之间想要进行数据传输,直接将数据发送给对端主机即可,但也意味着UDP协议是不可靠的,数据在传输过程中若出现了丢包、乱序等情况,UDP协议是不知情的


既然UDP协议是不可靠的,那为什么还要有UDP协议的存在?


可靠是需要做更多工作的,TCP协议虽然是一种可靠的传输协议,但TCP协议在底层需要做更多的工作,因此TCP协议底层的实现较为复杂


UDP协议虽然是一种不可靠的传输协议,但UDP协议在底层不需要做过多的工作,因此UDP协议底层的实现比TCP协议更为要简单,UDP协议虽然不可靠,但能够快速的将数据发送给对方


编写网络通信代码时具体采用TCP协议还是UDP协议,完全取决于上层的应用场景。若应用场景严格要求数据在传输过程中的可靠性,此时就采用TCP协议;若应用场景允许数据在传输出现少量丢包,那么优先选择UDP协议,因为UDP协议足够简单且足够快捷


注意: 一些优秀的网站在设计网络通信算法时,会同时采用TCP协议和UDP协议,当网络流畅时就使用UDP协议进行数据传输,而当网速不好时就使用TCP协议进行数据传输,动态的调整后台数据通信的算法


1.3 网络字节序

计算机在存储数据时存在大小端的概念:


大端模式: 数据的高字节内容保存在内存的低地址处,数据的低字节内容保存在内存的高地址处

小端模式: 数据的高字节内容保存在内存的高地址处,数据的低字节内容保存在内存的低地址处

若编写的程序只在本地机器上运行,是不需要考虑大小端问题的,同一台机器上的数据采用的存储方式是一样的,要么采用的都是大端存储模式,要么采用的都是小端存储模式。


但若涉及网络通信,那就必须考虑大小端的问题,否则对端主机识别出来的数据可能与发送端想要发送的数据是不一致的


例如,两台主机之间进行网络通信,发送端是小端机,接收端是大端机。发送端将发送缓冲区中的数据按内存地址从低到高的顺序发出后,接收端从网络中获取数据依次保存在接收缓冲区时,也是按内存地址从低到高的顺序保存的,但小端机与大端机对内存中数据的解读方式是不相同的

270da8d5dd5a43f7a267765ce1a09976.png



对于内存地址从低到高为 44332211 的序列,发送端按小端的方式识别出来为 0x11223344 ,而接收端按大端的方式识别出来为 0x44332211 ,此时接收端识别到的数据与发送端原本想要发送的数据不一样,这就是由于大小端的偏差导致数据识别出现了错误


如何解决大小端差异信息?


TCP/IP协议规定,网络数据流采用大端字节序,即低地址高字节。无论是大端机还是小端机,都必须按照TCP/IP协议规定的网络字节序来发送和接收数据


若发送端是小端,先将数据转成大端,然后再发送到网络中

若发送端是大端,则可直接进行发送

若接收端是小端,先将接收到数据转成小端后再进行数据识别

若接收端是大端,则可直接进行数据识别

如下,发送端是小端机,在发送数据前先将数据转成大端,然后再发送到网络中,而由于接收端是大端机,因此接收端接收到数据后可直接进行数据识别,此时接收端识别出来的数据就与发送端原本想要发送的数据相同了


be2edfe8823c4f8486282aa409c8b0d7.png


大部分的大小端的转化工作由操作系统来完成,该操作属于通信细节。也有部分的信息需要程序员自行进行处理,如端口号和IP地址


为什么网络字节序采用的是大端?而不是小端?


TCP在Unix时代就有了,以前Unix机器都是大端机,因此网络字节序采用的就是大端,但之后人们发现用小端能简化硬件设计,所以现在主流的机器都是小端机,但协议已经不便更改了

大端序更符合现代人的读写习惯

网络字节序与主机字节序之间的转换


为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数实现网络字节序和主机字节序之间的转换

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

函数名中的 h 表示host,n 表示network,l 表示32位长整数,s 表示16位短整数


如htonl()表示将32位长整数从主机字节序转换为网络字节序

若主机是小端字节序,则函数将参数做相应的大小端转换然后返回

若主机是大端字节序,则这些函数不做任何转换,将参数原封不动地返回

二、socket编程接口

2.1 常见socketAPI

创建套接字:(TCP/UDP,客户端+服务器)

int socket(int domain, int type, int protocol);

绑定端口号:(TCP/UDP,服务器)

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

监听套接字:(TCP,服务器)

int listen(int sockfd, int backlog);

接收请求:(TCP,服务器)

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

建立连接:(TCP,客户端)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

2.2 sockaddr结构

sockaddr结构的出现

套接字不仅支持跨网络的进程间通信,还支持本地的进程间通信(域间套接字)。在进行跨网络通信时需要传递的端口号和IP地址,而本地通信则不需要,因此套接字提供了sockaddr_in结构体和sockaddr_un结构体,sockaddr_in结构体用于跨网络通信,sockaddr_un结构体用于本地通信


为了让套接字的网络通信和本地通信能够使用同一套函数接口,于是就出现了 sockaddr 结构体,该结构体与sockaddr_in和sockaddr_un的结构都不相同,但这三个结构体头部的16个bit位一样,这个字段被称为协议家族

7ef4bd44d7c049b481b82ece9285dad6.png

此时传参时,就不用传入sockeaddr_in或sockeaddr_un结构体,而统一传入sockeaddr这样的结构体。在设置参数时就可以通过设置协议家族这个字段,来表明是要进行网络通信还是本地通信。在这些API内部会提取sockeaddr结构头部的16位进行识别,进而得出是要进行网络通信还是本地通信,然后执行对应的操作。此时就通过通用sockaddr结构,将套接字网络通信和本地通信的参数类型进行了统一


注意: 实际在进行网络通信时,定义的还是sockaddr_in结构体变量,只不过在传参时需要将该结构体变量地址的类型强转为sockaddr*


IPv4和IPv6的地址格式定义在 netinet/in.h 中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型,16位端口号和32位IP地址

IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6。只要取得sockaddr结构体的首地址,不需要知道是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容

socket API可以都用struct sockaddr* 类型表示,在使用的时候需要强制转化成sockaddr_in;这样的好处是程序的通用性,可以接收IPv4、IPv6,以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数

为什么不使用void*代替struct sockaddr*类型?


可以将这些函数的struct sockaddr*参数类型改为void*,此时在函数内部也可以直接指定提取头部的16个比特位进行识别,最终也能够判断是需要进行网络通信还是本地通信,那为什么还要设计出sockaddr这样的结构呢?


实际在设计这一套网络接口的时候C语言还不支持void*,于是就设计出了sockaddr这样的解决方案。并且在C语言支持了void*之后也没有将它改回来,因为这些接口是系统接口,系统接口是所有上层软件接口的基石,系统接口是不能轻易更改的,否则引发的后果是不可想的,这也就是为什么现在依旧保留sockaddr结构的原因


相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
目录
相关文章
|
5月前
|
开发者 Python
Python Socket编程:不只是基础,更有进阶秘籍,让你的网络应用飞起来!
在数字时代,网络应用成为连接世界的桥梁。Python凭借简洁的语法和丰富的库支持,成为开发高效网络应用的首选。本文通过实时聊天室案例,介绍Python Socket编程的基础与进阶技巧。基础篇涵盖服务器和客户端的建立与数据交换;进阶篇则探讨多线程与异步IO优化方案,助力提升应用性能。通过本案例,你将掌握Socket编程的核心技能,推动网络应用飞得更高、更远。
91 1
|
3月前
|
Kubernetes 网络协议 Python
Python网络编程:从Socket到Web应用
在信息时代,网络编程是软件开发的重要组成部分。Python作为多用途编程语言,提供了从Socket编程到Web应用开发的强大支持。本文将从基础的Socket编程入手,逐步深入到复杂的Web应用开发,涵盖Flask、Django等框架的应用,以及异步Web编程和微服务架构。通过本文,读者将全面了解Python在网络编程领域的应用。
67 1
|
4月前
|
消息中间件 监控 网络协议
Python中的Socket魔法:如何利用socket模块构建强大的网络通信
本文介绍了Python的`socket`模块,讲解了其基本概念、语法和使用方法。通过简单的TCP服务器和客户端示例,展示了如何创建、绑定、监听、接受连接及发送/接收数据。进一步探讨了多用户聊天室的实现,并介绍了非阻塞IO和多路复用技术以提高并发处理能力。最后,讨论了`socket`模块在现代网络编程中的应用及其与其他通信方式的关系。
463 3
|
4月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【10月更文挑战第10天】网络协议定义了机器间通信的标准格式,确保信息准确无损地传输。主要分为两种模型:OSI七层模型与TCP/IP模型。
|
5月前
|
网络协议 Python
告别网络编程迷雾!Python Socket编程基础与实战,让你秒变网络达人!
在网络编程的世界里,Socket编程是连接数据与服务的关键桥梁。对于初学者,这往往是最棘手的部分。本文将用Python带你轻松入门Socket编程,从创建TCP服务器与客户端的基础搭建,到处理并发连接的实战技巧,逐步揭开网络编程的神秘面纱。通过具体的代码示例,我们将掌握Socket的基本概念与操作,让你成为网络编程的高手。无论是简单的数据传输还是复杂的并发处理,Python都能助你一臂之力。希望这篇文章成为你网络编程旅程的良好开端。
78 3
|
5月前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
在数字宇宙中,网络如同复杂脉络连接每个角落,Python Socket编程则是开启这一世界的钥匙。本文将引导你从基础概念入手,逐步掌握Socket编程,并通过实战示例构建TCP/UDP服务器与客户端。你将学会使用Python的socket模块进行网络通信,了解TCP与UDP的区别,并运用多线程与异步IO提升服务器性能。跟随本文指引,成为网络世界的建筑师,构建自己的网络帝国。
48 2
|
5月前
|
网络协议 开发者 Python
网络编程小白秒变大咖!Python Socket基础与进阶教程,轻松上手无压力!
在网络技术飞速发展的今天,掌握网络编程已成为开发者的重要技能。本文以Python为工具,带你从Socket编程基础逐步深入至进阶领域。首先介绍Socket的概念及TCP/UDP协议,接着演示如何用Python创建、绑定、监听Socket,实现数据收发;最后通过构建简单的聊天服务器,巩固所学知识。让初学者也能迅速上手,成为网络编程高手。
93 1
|
5月前
|
网络协议 安全 网络安全
震惊!Python Socket竟能如此玩转网络通信,基础到进阶全攻略!
【9月更文挑战第12天】在网络通信中,Socket编程是连接不同应用与服务的基石。本文通过问答形式,从基础到进阶全面解析Python Socket编程。涵盖Socket的重要性、创建TCP服务器与客户端、处理并发连接及进阶话题如非阻塞Socket、IO多路复用等,帮助读者深入了解并掌握网络通信的核心技术。
231 6
|
5月前
|
消息中间件 网络协议 网络安全
解锁Python Socket新姿势,进阶篇带你玩转高级网络通信技巧!
【9月更文挑战第13天】在掌握了Python Socket编程基础后,你是否想进一步提升技能?本指南将深入探讨Socket编程精髓,包括从阻塞到非阻塞I/O以提高并发性能,使用`select`进行非阻塞操作示例;通过SSL/TLS加密通信保障数据安全,附带创建SSL服务器的代码实例;以及介绍高级网络协议与框架,如HTTP、WebSocket和ZeroMQ,帮助你简化复杂应用开发。通过学习这些高级技巧,你将在网络编程领域更进一步。
60 3
|
5月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【9月更文挑战第14天】网络协议是机器间交流的约定格式,确保信息准确传达。主要模型有OSI七层与TCP/IP模型,通过分层简化复杂网络环境。IP地址全局定位设备,MAC地址则在本地网络中定位。网络分层后,数据包层层封装,经由不同层次协议处理,最终通过Socket系统调用在应用层解析和响应。

热门文章

最新文章