Socket编程

源ip地址和目的ip地址 和端口号

ip地址是找到唯一的主机 端口号是找到主机上对应网络通信的进程

ip+port就可以找到唯一通信的主机进程

端口号:

端口号是一个16位的二进制整数,占用2字节存储空间。

  • 端口号用于标识特定进程,告知操作系统应将接收到的数据交由哪个进程处理;
  • 通过IP地址与端口号的组合,可以精确定位网络中某台主机的特定进程;
  • 每个端口号在同一时刻只能被一个进程独占使用。

一个进程可以有多个端口,而一个端口只能绑定一个进程。

Socket

  • IP地址与端口号的组合(IP+Port)能够唯一标识互联网中的一个进程
  • 网络通信本质上是通过互联网进程实现的,可通过{源IP,源端口,目标IP,目标端口}这组四元组来精确定位通信双方
  • 因此,网络通信本质上也是进程间通信的一种形式
  • 我们将IP地址与端口号的组合称为套接字(socket)

网络字节序

由于内存中的多字节数据有大端小端的区分,网络数据流同样有大小端的区分。但收发之间应该统一,所以TCP/IP协议规定网络字节序按照大端收发,如果发送主机是小端就转换成大端后发送。

主机字节序和网络字节序的转换

c 复制代码
uint32_t htonl;  // Convert host long to network byte order
uint16_t htons;  // Convert host short to network byte order

uint32_t ntohl;  // Convert network long to host byte order
uint16_t ntohs;  // Convert network short to host byte order
 

• 若主机采用小端字节序,这些函数会先将参数进行大小端转换,随后返回结果。

Socket编程API

c 复制代码
// 创建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);
socket参数:
domain (地址族/协议族)

指定通信使用的协议族,常见取值包括:

  • AF_INET:IPv4协议,最常用的互联网协议
  • AF_INET6:IPv6协议,下一代互联网协议
  • AF_UNIXAF_LOCAL:本地通信协议,用于同一主机上的进程间通信
  • AF_PACKET:底层数据包接口,允许直接访问网络层
  • AF_NETLINK:内核与用户空间通信接口
type (套接字类型)

指定通信的语义,常见取值包括:

  • SOCK_STREAM:面向连接的字节流套接字(TCP)
  • SOCK_DGRAM:无连接的数据报套接字(UDP)
  • SOCK_RAW:原始套接字,允许直接访问底层协议
  • SOCK_SEQPACKET:固定长度的、有序的、可靠的基于连接的数据传输
protocol (具体协议)

指定使用的具体协议,通常设为0让系统根据前两个参数自动选择,但也可以显式指定:

  • IPPROTO_TCP:TCP传输协议
  • IPPROTO_UDP:UDP传输协议
  • IPPROTO_ICMP:ICMP协议(常用于ping)
  • IPPROTO_RAW:原始IP数据包
bind参数详解:
socket参数:
  • 这是一个已经创建的套接字描述符
address参数:
  • 这是一个指向sockaddr结构的指针
  • 包含要绑定的IP地址和端口号
address_len参数:
  • 指定address结构体的长度
  • 通常使用sizeof()运算符获取
Listen参数详解
bindlog 是并发连接数 指定最大的监听队列长度 决定了可以连接的最大长度
Accept和Bind参数

accept和connection和bind接口参数类似

Sockaddr结构

sockaddr 是网络编程中用于表示套接字地址的基础结构体,根据不同的协议族和使用场景,主要分为以下三种类型:

  1. struct sockaddr

    通用的套接字地址结构体,定义在 <sys/socket.h> 中。

    • 包含 sa_family(地址族,如 AF_INETAF_UNIX)和 sa_data(地址数据)。
    • 通常作为参数类型用于套接字函数(如 bind()connect()),实际使用时需转换为具体类型(如 sockaddr_in)。
  2. struct sockaddr_in

    IPv4 地址专用结构体,定义在 <netinet/in.h>

    • 包含 sin_family(地址族,固定为 AF_INET)、sin_port(16位端口号,需网络字节序)、sin_addr(IPv4 地址,struct in_addr 类型)。

    • 示例:绑定到 0.0.0.0:8080:

      c 复制代码
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(8080);
      addr.sin_addr.s_addr = INADDR_ANY;
  3. struct sockaddr_un

    Unix 域套接字(本地进程间通信)地址结构体,定义在 <sys/un.h>

    • 包含 sun_family(地址族,固定为 AF_UNIX)和 sun_path(文件路径名)。

    • 示例:绑定到 /tmp/my_socket

      c 复制代码
      struct sockaddr_un addr;
      addr.sun_family = AF_UNIX;
      strcpy(addr.sun_path, "/tmp/my_socket");

应用场景差异

  • sockaddr_in 用于 TCP/UDP 网络通信(如 Web 服务)。
  • sockaddr_un 用于同一主机内高效进程间通信(如数据库客户端/服务端)本地通信。
  • 强制类型转换是常见做法:调用 bind() 时需将 sockaddr_insockaddr_un 指针转换为 sockaddr*。此举类似于C++中的多态继承机制 父类指针指向子类对象;

UDP套接字基本流程详解

创建UDP套接字

c 复制代码
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// AF_INET表示IPv4协议
// SOCK_DGRAM表示数据报套接字(UDP)
// 返回值是套接字描述符,失败返回-1

初始化本地地址结构

c 复制代码
struct sockaddr_in local;
memset(&local, 0, sizeof(local));  // 清空结构体
local.sin_family = AF_INET;        // 地址族
local.sin_port = htons(_port);     // 端口号(网络字节序)
local.sin_addr.s_addr = INADDR_ANY; // 监听所有网络接口

绑定套接字

c 复制代码
int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
// 将套接字与本地地址绑定
// 成功返回0,失败返回-1

接收数据

c 复制代码
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, 
        (struct sockaddr *)&peer, &len);
// 接收数据并保存发送方地址信息
// buffer: 接收缓冲区
// 最后一个参数是地址结构长度(输入输出参数)

发送数据

c 复制代码
sendto(_sockfd, buffer, strlen(buffer), 0, 
      (struct sockaddr *)&peer, len);
// 向指定地址发送数据
// peer: 目标地址结构
// len: 地址结构长度

TCP套接字基本流程详解

服务器端流程

c 复制代码
// 1. 创建套接字
int listenfd = socket(AF_INET, SOCK_STREAM, 0);

// 2. 设置地址重用
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

// 3. 绑定地址
struct sockaddr_in servaddr;
// ...初始化servaddr...
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

// 4. 监听连接
listen(listenfd, 5);  // 5是等待队列长度

// 5. 接受连接
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
int connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &len);

// 6. 收发数据
recv(connfd, buffer, sizeof(buffer), 0);
send(connfd, buffer, strlen(buffer), 0);

// 7. 关闭连接
close(connfd);

客户端流程

c 复制代码
// 1. 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);

// 2. 连接服务器
struct sockaddr_in servaddr;
// ...初始化servaddr...
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

// 3. 收发数据
send(sockfd, buffer, strlen(buffer), 0);
recv(sockfd, buffer, sizeof(buffer), 0);

// 4. 关闭连接
close(sockfd);

主要区别:

  • TCP需要建立连接(connect/accept),UDP不需要
  • TCP使用send/recv,UDP使用sendto/recvfrom
  • TCP保证可靠传输,UDP不保证
  • TCP是面向流的,UDP是面向数据报的
相关推荐
了不起的云计算V2 小时前
内存/SSD、CPU供应链压力传导,服务器整机或迎新一轮涨价潮
运维·服务器
WJ.Polar2 小时前
华为OSPF配置实战详解
运维·网络
吴秋霖2 小时前
某网站WebSocket协议逆向分析
网络·websocket·网络协议
_OP_CHEN2 小时前
【Linux系统编程】(十九)深入 Linux 文件与文件 IO:从底层原理到实战操作,一文吃透!
linux·运维·操作系统·系统文件·系统调用·c/c++·文件i/o
Godspeed Zhao3 小时前
现代智能汽车中的无线技术39——V2X(11)
网络·汽车
Irene19913 小时前
手机SIM卡 4G 5G 信号强度 和 移动网络 WIFI 之间的关系或区别
网络·5g·4g·sim卡
深信达沙箱4 小时前
SDC沙箱能够满足哪些场景需求?
网络·加密·软件·源代码·沙盒
杜子不疼.4 小时前
【Linux】基础IO(二):系统文件IO
linux·运维·服务器
郝学胜-神的一滴4 小时前
深入理解网络IP协议与TTL机制:从原理到实践
linux·服务器·开发语言·网络·网络协议·tcp/ip·程序人生