TCP网络编程基础

一、套接字地址结构

socket编程需要指定socket地址,不同的协议族有不同的地址定义方式以及由此带来的结构上的差异。这些地址结构的名称形如sockaddr_xxx。每个协议族都有一个唯一的后缀。例如,对于以太网,其结构为sockaddr_in。

(一)通用套接字数据结构

sa_family_t 是一个用于表示地址族(Address Family)的数据类型,在网络编程中用于标识套接字所使用的协议类型。sa_family_t 的具体定义如下:

  • 类型定义 ‌:sa_family_t 通常被定义为 unsigned short int(无符号短整型),占用 2 个字节。‌

  • 作用 ‌:该类型用于结构体(如 struct sockaddrstruct sockaddr_instruct sockaddr_in6 等)中的 sa_family 成员,以指定地址族的类型。‌

  • 常见取值 ‌:

    • AF_INET:表示 IPv4 协议族。
    • AF_INET6:表示 IPv6 协议族。
    • AF_UNIXAF_LOCAL:表示本地域套接字(用于同一台机器上的进程间通信)。‌

例如,在 IPv4 套接字地址结构 struct sockaddr_in 中,sin_family 成员即为 sa_family_t 类型,用于标识该地址为 IPv4 地址。‌

此外,sa_family_t 的设计使得网络函数(如 bind()connect())能够通过该字段判断传入地址结构的具体类型,并进行相应的类型转换处理。‌

(二)IPV4套接字数据结构

(三)sockaddr和sockaddr_in的关系

sockaddrsockaddr_in 是网络编程中用于表示网络地址的两个关键结构体,它们的关系可以概括为:‌**sockaddr_insockaddr 的一个具体实现,专门用于 IPv4 地址。**‌

简单来说,sockaddr 是一个通用的地址容器,而 sockaddr_in 是为 IPv4 协议量身定制的、更具体的地址结构。

1‌.通用 vs. 具体

  • ‌**sockaddr** ‌:这是一个‌通用 ‌的套接字地址结构体,定义在 <sys/socket.h> 中。它的设计初衷是能容纳各种类型的网络地址(如 IPv4、IPv6、Unix 域套接字等)。它只有两个字段:sa_family(地址族,如 AF_INET 表示 IPv4)和 sa_data(14字节的协议特定地址数据)。由于 sa_data 是一个不透明的字节数组,直接使用它来设置 IP 地址和端口非常不方便。

  • ‌**sockaddr_in** ‌:这是一个‌具体 ‌的结构体,定义在 <netinet/in.h> 中,专门用于表示 IPv4 地址。它包含了清晰、易用的字段:sin_family(固定为 AF_INET)、sin_port(端口号)、sin_addr(IP 地址结构体)和 sin_zero(填充字段)。程序员通常直接操作 sockaddr_in 来设置 IPv4 地址信息。

2‌.结构体大小

为了确保类型安全和兼容性,sockaddr_in 的设计保证了其总大小与 sockaddr 完全相同,都是 16 字节。sin_zero 字段(8字节)的存在就是为了填充,使两个结构体的大小一致。

‌3.使用方式

在实际编程中,你几乎总是直接使用 sockaddr_in 来构建 IPv4 地址信息,因为它字段清晰,易于理解和操作。然而,系统调用函数(如 bind(), connect(), accept())的参数类型是 struct sockaddr *。因此,你需要将 sockaddr_in 结构体的指针‌强制转换 ‌为 sockaddr 指针后,再传递给这些函数。

(四)相互转换

cpp 复制代码
#include <sys/socket.h>
#include <netinet/in.h>

struct sockaddr_in my_addr;
// ... 初始化 my_addr 的 sin_family, sin_port, sin_addr 等字段 ...

// 将 sockaddr_in 转换为 sockaddr,用于系统调用
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));

// 反向转换(例如从 accept() 获取客户端地址时)
struct sockaddr client_addr;
socklen_t addr_len = sizeof(client_addr);
accept(sockfd, &client_addr, &addr_len);

// 将获取到的 sockaddr 转换回 sockaddr_in 以便访问
struct sockaddr_in *client_in = (struct sockaddr_in *)&client_addr;
printf("Client IP: %s\n", inet_ntoa(client_in->sin_addr));
printf("Client Port: %d\n", ntohs(client_in->sin_port));

二、套接字编程流程

(一)TCP流程

(二)UDP流程

三、socket函数介绍

socket()用于创建一个套接字。

(一)函数原型

cpp 复制代码
#include <sys/socket.h>
 
int socket(int domain, int type, int protocol);

(二)参数详解

1. domain (协议域/地址族)

指定套接字使用的通信协议族,决定了套接字的地址格式和通信方式。常见选项:

  • AF_UNIX Unix域套接字 本地进程间通信(IPC)
  • AF_INET IPv4互联网协议族 IPv4网络通信
  • AF_INET6 IPv6互联网协议族 IPv6网络通信
  • AF_IPX IPX协议族(已废弃) Novell网络
  • AF_NETLINK 内核用户接口设备 用户与内核通信
  • AF_X25 X.25协议族(已废弃) X.25网络

2. type (套接字类型)

指定套接字的通信语义类型,常见选项:

  • SOCK_STREAM 面向连接的可靠字节流 TCP 双向、可靠、有序、无重复
  • SOCK_DGRAM 无连接的数据报服务 UDP 不可靠、无序、可能有重复
  • SOCK_RAW 原始套接字 IP 可访问底层协议头
  • SOCK_SEQPACKET 面向连接的有序可靠数据报 SCTP等 类似SOCK_STREAM但保持记录边界
  • SOCK_RDM 可靠交付消息 较少使用 提供可靠的消息传递

3.protocol (协议类型)

指定具体的协议,通常设置为0表示自动选择:

  • IPPROTO_TCP TCP协议(通常与SOCK_STREAM配合)
  • IPPROTO_UDP UDP协议(通常与SOCK_DGRAM配合)
  • IPPROTO_SCTP SCTP协议

4.返回值

成功:返回一个非负整数,即套接字文件描述符(socket descriptor)

失败:返回-1,并设置errno表示具体错误,常见错误包括:

  • EACCES:权限不足
  • EAFNOSUPPORT:不支持指定的地址族
  • EMFILE:进程打开的文件描述符过多
  • ENFILE:系统全局文件描述符不足
  • ENOBUFS或ENOMEM:内存不足
  • EPROTONOSUPPORT:不支持指定的协议

四、TCP相关函数

(一)bind()

TCP服务端用于将用于通信的地址和端口绑定到 socket 上。函数的参数应该包含:用于通信的 socket 和服务端的 IP 地址和端口号。ip地址和端口号是放在 socketaddr_in 结构体里面的。

cpp 复制代码
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

1.参数说明

  • sockfd :需要绑定的socket。
  • addr :存放了服务端用于通信的地址和端口。
  • addrlen:表示 addr 结构体的大小

2.返回值

  • 成功:返回 0
  • 失败:返回 -1 并设置 errno

(二)listen()

cpp 复制代码
#include <sys/socket.h>

int listen(int sockfd, int backlog);

1.参数说明

  • sockfd
    已绑定的套接字文件描述符(通过 socket() 创建并已调用 bind()
  • backlog
    等待连接队列的最大长度(内核为该套接字排队的最大连接数)

2.返回值

  • 成功:返回 0
  • 失败:返回 -1 并设置 errno
相关推荐
zjeweler2 小时前
开发者资源一站式获取:高效查找服务器折扣与学习资料指南
运维·服务器
唔662 小时前
mDNS 就是局域网里的“零配置DNS“
网络·智能路由器
Hello_Embed2 小时前
嵌入式上位机开发入门(二十九):JsonRPC TCP Server
网络·单片机·网络协议·tcp/ip·json·嵌入式
南境十里·墨染春水2 小时前
linux学习进展 网络基础
linux·网络·学习
实心儿儿2 小时前
Linux —— 基础IO - 一切皆文件 + 缓冲区
linux·运维·服务器
Rust研习社2 小时前
Reqwest 兼顾简洁与高性能的现代 HTTP 客户端
开发语言·网络·后端·http·rust
大熊背2 小时前
ISP Pipeline中Lv实现方式探究之六--lv值计算再优化
网络·算法·自动曝光·lv
zjeweler2 小时前
云服务搭建游戏服务器实战指南
运维·服务器·游戏
RTC老炮2 小时前
WebRTC下FlexFEC算法架构及原理
网络·算法·音视频·webrtc