【Linux】探索网络编程:TCP/UDP协议解析与Socket应用实例

前言:

在现代信息技术飞速发展的今天,网络通信已经成为我们日常生活和工作中不可或缺的一部分。无论是通过电子邮件、社交媒体还是在线会议,网络通信都扮演着至关重要的角色。而在这背后,是复杂的网络协议和编程技术支撑着这一切的运行。本文旨在深入探讨网络编程的基础知识,特别是UDP和TCP这两种常用的传输层协议,以及它们在socket编程中的应用。通过本文,读者将能够理解源IP地址、目的IP地址、端口号等概念,并学习如何使用socket编程接口来创建网络应用程序。此外,本文还将介绍网络字节序的概念,以及如何通过转换函数确保网络程序的可移植性。最后,通过一个简单的UDP网络程序实例,我们将展示如何实现一个回声服务器和简易聊天室,让读者对网络编程有更直观的认识。

1. 预备知识

1.1 理解源IP地址和目的IP地址

在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址。

思考: 我们光有IP地址就可以完成通信了嘛? 想象一下发qq消息的例子, 有了IP地址能够把消息发送到对方的机器上,但是还需要有一个其他的标识来区分出, 这个数据要给哪个程序进行解析。

1.2 认识端口号

  1. 我们上网,无非两种动作:a. 把远端的数据拉取到本地 b. 把我的数据发送到远端
  2. 大部分的网络通信行为,都是用户触发的。计算机中,谁表示用户呢?进程!(客户端服务,服务端服务)
  3. 把数据发送到目标主机,不是目的,是手段。正真的目的,是把数据交给这个主机上的某一个服务(进程)(服务必须具有唯一的标识:端口号)
  4. 网络通信的本质,其实是进程帮我们进行网络通信,无论是对于C还是S
  5. IP(唯一的一台主机)+ port(该主机的唯一的一个进程) = 互联网中唯一的一个进程
  6. client -> server: client进程 -> server进程
    client进程 = client ip + client port = client是互联网中唯一的一个进程
    server进程 = server ip + server port = server是互联网中唯一的一个进程
    唯一的找到彼此(src ip, src port; dst ip, dst port)(socket通信)

结论: 网络通信的本质:其实就是进程间通信!
进程间通信:看到公共的资源(网络)

1.3 理解"端口号"和"进程ID"

我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么这两者之间是怎样的关系?

  • PID 是操作系统用来标识一个进程的唯一编号。
  • 端口号 是网络通信中用来标识主机上特定服务的数字。

它们之间没有直接关系,PID用于操作系统内部管理,而端口号用于网络通信。另外, 一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定。

1.4 理解源端口号和目的端口号

传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要发给谁”;

1.5 认识TCP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识; 后面我们再详细讨论TCP的一些细节问题.

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

1.6 认识UDP协议

此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识; 后面再详细讨论.

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

1.6 TCP vs UDP 可靠性

TCP 要保证可靠性,就需要做更多的工作——TCP协议一定更复杂——接口会更多一些。
UDP 协议一定更简单。

1.7 网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

在这里插入图片描述
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
在这里插入图片描述

  • 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回 ;
  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

2. socket 编程接口

2.1 socket 常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
 socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,   socklen_t addrlen);

2.2 sockaddr结构

socket编程,是有不同种类的,有的是专门用来进行本地通信的(unix socket),有的是用来专门跨网络通信的(inet socket),有的是用来进行网络管理的(raw socket)。

统一接口 -> C语言写的 -> 统一类型 -> struct sockaddr

在这里插入图片描述

3. 简单的UDP网络程序

bind: socket = ip + port; 文件信息和网络信息给关联起来

   #include <sys/types.h>          /* See NOTES */
   #include <sys/socket.h>

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

填充信息:
在这里插入图片描述

sin_addr —— 16位地址类型: AF_INET、 32位IP地址
sin_port —— 16位端口号
sin_zero —— 8字节填充

在这里插入图片描述
如何理解 “192.168.1.2” <==> 4字节IP地址之间互相转换

// 4字节 转 字符串
struct IP
{
	uint8_t p1;
	uint8_t p2;
	uint8_t p3;
	uint8_t p4;
}

struct IP *temp = (struct IP*)&ipaddr;
to_string(temp->p1) + "." + to_string(temp->p2) + ...
// 字符串 转 4字节
struct IP temp;
temp.p1 = stoi(substr("."));
uint32_t ipint = (int)temp;

127.0.0.1 : 本地环回
在这里插入图片描述

./udpserver 127.0.0.1 8888

127.0.0.1 : 本地环回,可以实现本地通信,常用于进行代码测试
在这里插入图片描述
recvfrom(接收消息)

// 接收信息
   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);
   		// buf: 输出型缓冲区
   		// len: 期望长度
   		// 返回值: 实际读到的长度
   		// src_addr: 输出型参数(ip 与 port)
   		// addrlen: 输出型参数

有人给你发了消息,你想不想知道谁给你发的? 为什么? 因为我还要给别人回消息
你通过什么信息,只到只到对方是谁? socket 对方的 IP 和 port!

sendto(发送消息)

// 发送信息
   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);

使用公网ip,我们的服务器,无法直接bind公网ip(云服务器不允许),也严重不推荐,bind公网ip,或者任何一个确定的ip。
在这里插入图片描述

实现一个回声服务器:
在这里插入图片描述
在这里插入图片描述
终端输出也是一个文件,不同的终端窗口是不同的文件。
在这里插入图片描述

3.1 UDP实现简易聊天室:

gitee:https://gitee.com/q-haodong/test_-linux/tree/master/20240702_udp_echo_sever
在这里插入图片描述
在这里插入图片描述

总结:

本文从网络通信的基本概念出发,逐步深入到TCP和UDP协议的细节,并探讨了它们在socket编程中的应用。通过预备知识的介绍,我们理解了源IP地址、目的IP地址、端口号等在网络通信中的重要性。同时,我们也学习了网络字节序的概念,以及如何通过特定的函数进行主机字节序和网络字节序之间的转换,以确保程序的兼容性和可移植性。在socket编程接口部分,我们介绍了创建socket文件描述符、绑定端口号、监听socket、接收请求、建立连接等常见API的使用。最后,通过实现一个UDP回声服务器和简易聊天室的示例,我们展示了UDP协议在实际网络编程中的应用。通过本文的学习,读者不仅能够掌握网络编程的基础知识,还能够获得实际编程的经验,为进一步深入学习和探索网络编程打下坚实的基础。

  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q_hd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值