当前位置: 首页 > news >正文

Linux网络:socket编程UDP

文章目录

  • 前言
  • 一,socket
  • 二,服务端socket
    • 3-1 创建socket
    • 3-2 绑定地址和端口
    • 3-3 接收数据
    • 3-4 回复数据
    • 3-5关闭socket
    • 3-6 完整代码
  • 三,客户端socket
    • 3-1 为什么客户端通常不需要手动定义 IP 和端口


前言

学习 socket 编程的意义在于:它让你掌握计算机之间通信的核心原理,能亲手实现聊天程序、文件传输、简易服务器等网络应用;同时这是理解 TCP/IP 协议、深入系统编程和进入后台开发、分布式系统的基础技能,也是面试和工程实践中必不可少的知识。


一,socket

socket(套接字) 是操作系统提供的一种 通信机制,最常用于网络通信,在编程时,你可以把它当作 特殊的文件描述符(FD),既能读、也能写,文件用来在本地磁盘读写数据,而 socket 用来在不同主机之间交换数据。你可以把 socket 看作网络文件,只是读写的数据不是在磁盘上,而是发送到网络上的另一个进程。


二,服务端socket

主要作用:接收客户端请求并回复,服务器端使用socket分为几个阶段:创建 socket -> 绑定地址和端口 -> 等待客户端发送数据 / 监听连接 -> 处理数据或回复 -> 关闭 socket


3-1 创建socket

socket函数原型

int socket(int domain, int type, int protocol);
  • domain:用于选择网络协议协议类型一般有值:AF_INET (IPv4 网络协议),AF_INET6 (IPv6 网络协议),AF_UNIX/AF_LOCAL (本地进程间通信)
  • type:指定传输方式,主要有两种:SOCK_STREAM 面向连接,基于字节流(TCP),SOCK_DGRAM 无连接,基于报文(UDP)
  • protocol:通常指定具体协议:0 默认协议,IPPROTO_TCP 明确选择 TCP,IPPROTO_UDP 明确选择 UDP

在下面的代码示例中我们会选择:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

创建一个UDP的套接字


3-2 绑定地址和端口

struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY; // 任意 IP
servaddr.sin_port = htons(12345);      // 端口
bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));

上面的struct sockaddr_in servaddr创建一个 IPv4 地址结构体 sockaddr_in,用来保存服务端的 IP 地址和端口信息结构体定义大致如下:

struct sockaddr_in {short            sin_family;   // 地址族 (AF_INET)unsigned short   sin_port;     // 端口号struct in_addr   sin_addr;     // IP 地址char             sin_zero[8];  // 填充为与 struct sockaddr 同大小
};

解释上述代码:

servaddr.sin_family = AF_INET;

设置 地址族为 IPv4 (AF_INET)告诉内核这是一个 IPv4socket

servaddr.sin_addr.s_addr = INADDR_ANY; // 任意 IP

INADDR_ANY = 0.0.0.0,表示服务端 绑定本机所有可用 IP 地址,如果服务器有多个网卡,客户端发送到任意 IP 都能被接收

servaddr.sin_port = htons(12345); // 端口

设置端口号为 12345htons = host to network short(主机字节序 → 网络字节序),网络通信使用 大端序,保证不同平台可以正确解析端口

bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));

bind() socket 文件描述符 与 IP + 端口 绑定

参数解释:

sockfd:之前 socket() 返回的文件描述符

(struct sockaddr*)&servaddr : 地址信息,强制类型转换为通用 sockaddr

sizeof(servaddr) : 结构体大小


3-3 接收数据

char buffer[100];
struct sockaddr_in cliaddr {};
socklen_t len = sizeof(cliaddr);int n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr * ) & cliaddr, & len);

这段代码在一个 UDP 服务器程序中,从客户端接收数据,存储到 buffer 中,同时记录客户端的地址信息cliaddr,并返回接收的字节数n

char buffer[100];

定义一个字符数组 buffer,大小为 100 字节,用来存储从客户端接收的数据

struct sockaddr_in cliaddr {};

定义一个 sockaddr_in 结构体变量 cliaddr,用于存储客户端的地址信息(如 IP 地址和端口号)。{} 初始化所有字段为 0

socklen_t len = sizeof(cliaddr);

定义一个 socklen_t 类型的变量 len,初始化为 cliaddr 的大小(通常 16 字节,struct sockaddr_in 的大小)。


3-4 回复数据

const char* reply = "Hello Client";sendto(sockfd, reply, strlen(reply), 0, (struct sockaddr*)&cliaddr, len); // 回复

这段代码用与服务器回复客户端信息

其它的不再过多赘述

(struct sockaddr*)&cliaddr:客户端地址结构体,告诉内核消息发送到哪里


3-5关闭socket

close(sockfd);

不关闭会造成资源泻漏


3-6 完整代码

#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>int main() {int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建 UDP socketif (sockfd < 0) { perror("socket"); return -1; }sockaddr_in servaddr{};servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = INADDR_ANY;servaddr.sin_port = htons(12345);bind(sockfd, (sockaddr*)&servaddr, sizeof(servaddr)); // 绑定端口char buffer[100];sockaddr_in cliaddr{};socklen_t len = sizeof(cliaddr);int n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (sockaddr*)&cliaddr, &len); // 接收消息buffer[n] = '\0';std::cout << "Server received: " << buffer << std::endl;const char* reply = "Hi Client";sendto(sockfd, reply, strlen(reply), 0, (sockaddr*)&cliaddr, len); // 回复客户端close(sockfd);return 0;
}

三,客户端socket

客户端socket代码我直接展示出来吧,我们理解了服务端之后,客户端是非常好理解的

#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>int main() {int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建 UDP socketif (sockfd < 0) { perror("socket"); return -1; }sockaddr_in servaddr{};servaddr.sin_family = AF_INET;servaddr.sin_port = htons(12345);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 本机测试const char* msg = "Hello Server";sendto(sockfd, msg, strlen(msg), 0, (sockaddr*)&servaddr, sizeof(servaddr)); // 发送消息char buffer[100];socklen_t len = sizeof(servaddr);int n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (sockaddr*)&servaddr, &len); // 接收回复buffer[n] = '\0';std::cout << "Client received: " << buffer << std::endl;close(sockfd);return 0;
}

这里的sendtorecvfrom大家应该都懂,主要是这个客户端是如何找到服务端的重要

    sockaddr_in servaddr{};servaddr.sin_family = AF_INET;servaddr.sin_port = htons(12345);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 本机测试

在这段代码里的IP地址和端口号是找到服务端的关键

  • IP 地址 (127.0.0.1):告诉客户端要把数据发送到哪个机器。127.0.0.1 表示本机,也就是服务端和客户端在同一台电脑。如果服务端在另一台电脑,你就要写它的真实 IP,例如 192.168.1.100

  • 端口号 (12345)
    服务端在这个端口上监听数据。UDP/TCP 都是靠端口区分不同服务的,就像房子门牌号一样。所以客户端通过 IP, port 就能找到服务端。


3-1 为什么客户端通常不需要手动定义 IP 和端口

客户端的职责是 主动找服务端,在调用 sendto()时:目标地址(服务端的 IP+端口) 由程序员指定,源地址(客户端的 IP+端口) 不写时由内核自动分配

IP:自动选择一块能到达服务端的本地网卡的 IP

端口:自动分配一个 临时端口,通常在 49152–65535 之间

客户端什么时候需要绑定 IP+端口:如果客户端希望 使用固定端口(比如做 P2P、游戏服务器通知端口),或者有多张网卡,必须指定 用哪张网卡的 IP 去通信,这种情况才会手动调用 bind()


文章转载自:

http://nllrzDRa.wmfny.cn
http://sq8xyRn0.wmfny.cn
http://DLlvRbrs.wmfny.cn
http://8ojvMQH1.wmfny.cn
http://nWLtdSHK.wmfny.cn
http://KHm84uQc.wmfny.cn
http://EjYpBGaZ.wmfny.cn
http://1GFwqhFb.wmfny.cn
http://gESOKki2.wmfny.cn
http://JUXKpceK.wmfny.cn
http://MKp3V9HG.wmfny.cn
http://cpdxQTKP.wmfny.cn
http://rGwrc42i.wmfny.cn
http://YRkreyyI.wmfny.cn
http://Z947haek.wmfny.cn
http://GQolRtJi.wmfny.cn
http://FU0RJNz8.wmfny.cn
http://JlQVlzIq.wmfny.cn
http://ac0yvAF3.wmfny.cn
http://JIDpvCYQ.wmfny.cn
http://rTQ5BLhJ.wmfny.cn
http://JTOzOn5X.wmfny.cn
http://qRkaLQW8.wmfny.cn
http://MDnNMKSD.wmfny.cn
http://A3PuQhM2.wmfny.cn
http://WSZfnKxg.wmfny.cn
http://T6YONdDo.wmfny.cn
http://nQFPgEj0.wmfny.cn
http://zkBiIAW3.wmfny.cn
http://8KoIyKHO.wmfny.cn
http://www.dtcms.com/a/383899.html

相关文章:

  • GeoHash分级索引技术
  • RISC与CISC:ARM指令集解析
  • 第十二篇:Qcom Camx打印实时帧率 FPS
  • 【开题答辩全过程】以 “候鸟式养老机构”管理系统的设计与实践为例,包含答辩的问题和答案
  • 造车阶段解读
  • 技术论文分析分析论文《计算机病毒判定专家系统原理与设计》思考其在游戏中的应用
  • Elasticsearch面试精讲 Day 18:内存管理与JVM调优
  • Android开发-文本输入
  • C++启航:从0到1,解锁面向对象编程的第一把密钥
  • 基于Dash和Plotly的交互式人体肌肉评分可视化系统[附源码】
  • Linux 开发工具(2)
  • Java进阶教程,全面剖析Java多线程编程,什么是多线程,笔记01
  • 论文参考文献交叉引用+中括号变成上标+自动生成目录方法
  • Linux:8_库制作与原理
  • Codeforces Round 1047 Div.3 DEFG补题
  • OWASP Top 10 最新版
  • 【脑电分析系列】第9篇:时频分析利器 — 小波变换与事件相关谱扰动(ERSP)的应用
  • struct的一些函数以及其他用法(析构、友元、构造、成员等)
  • c语言中实现线程同步的操作
  • 【Java后端】Spring Boot 2.7.x 和 Swagger 3.0.x (springfox 3.x) 的兼容性问题
  • Springboot的自动配置原理?
  • 9 月 13 日科技前沿大揭秘:多领域创新闪耀
  • 基于少样本支持的一类学习的增量式生成对抗诊断:
  • TDengine 特殊选择函数 UNIQUE 用户手册
  • 状态机SMACH相关教程介绍与应用案例分析——机器人操作进阶系列 · 状态机篇
  • Transformer简介
  • 维星AI-AI驱动的精准获客:重塑数字营销新范式
  • 视觉SLAM第11讲:回环检测
  • Linux相关概念和易错知识点(45)(网络层、网段划分)
  • 因果推断 | 从因果树到因果森林:理论解析与代码实践