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

Linux套接字Socket

Linux套接字Socket

前提知识补充

  • 为不同机器上的两个进程之间提供通信机制

  • 主机字节序小端存储,网络字节序大端存储

  • 特点TCPUDP
    连接类型面向连接无连接
    可靠性
    有序性保证数据包按顺序到达不保证数据包顺序
    流量控制有滑动窗口机制
    拥塞控制有拥塞控制机制
    复杂性较高较低
    实时性较差较好
    适用场景文件传输、Web 应用、电子邮件等实时音视频、在线游戏、DNS 查询等

Socket

前情提示

inet_addr函数
  • 将点分十进制格式的 IPv4 地址字符串转换为网络字节序的 32 位无符号整数
接口代码
#include <arpa/inet.h>in_addr_t inet_addr(const char* ip_address);
参数解释
  • ip_address: 点分十进制IPV4
返回值
  • 网络字节序的 32 位无符号整数
实例代码
#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>int main() {// 定义IPV4地址const char *ip_address = "192.168.1.1";in_addr_t ip;// 将点分十进制格式的 IP 地址转换为 32 位无符号整数ip = inet_addr(ip_address);if (ip == INADDR_NONE) {perror("inet_addr");printf("无效的 IP 地址: %s\n", ip_address);return 1;}// 打印转换后的 IP 地址printf("IP 地址: %s\n", ip_address);printf("转换后的 32 位无符号整数: 0x%08X\n", (unsigned int)ip);return 0;
}
套接字结构定义
#include <netine/in.h>struct sockaddr_in
{// 地址族,对于IPV4地址应设置为 AF_INETsa_family_t sin_family;// 16位端口号,以大端字节序存储in_port_t sin_port;// 32位IPV4地址,以大端字节序存储,指明通信所使用的IP地址/*** struct in_addr {*       in_addr_t s_addr;  /* 32 位无符号整数,表示 IP 地址 */* };*/struct in_addr sin_addr;// 填充字段,一般设置为0unsigned char sin_zero[8]
}
字节序
  • 将16位和32位整数在主机字节序和网络字节序之间进行转换
接口代码
#include <netinet/in.h>// h:hostname -- 本地 
// to:to -- 转变
// n:network -- 网络
// l:32位
// s:16位
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned short int netlong);
unsigned short int ntohs(unsigned short int netshort);
gethostname函数
  • 获取主机名
接口代码
#include <unistd.h>int gethostname(char* name,int namelength);
参数解释
  • name: 自定义缓冲区
  • namelength: 自定义缓冲区大小
返回值
  • 成功时返回 0。
  • 失败时返回 -1,并设置 errno 以指示错误原因
实例代码
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main()
{char buf[256];int res;res = gethostname(buf, sizeof(buf));if (res == 0)printf("本机地址为: %s\n", buf);return 0;
}
地址转换打印函数inet_ntoa
  • 将网络字节转换位可打印四点表示法格式的字符串
接口代码
#include <arpa/inet.h>char* inet_ntoa(struct in_addr in);
参数解释
  • in: 32位无符号整数
返回值
  • 成功时,返回一个指向静态分配的字符数组的指针,该数组包含点分十进制格式的 IP 地址字符串。
  • 如果输入无效,则返回 NULL
实例代码
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>int main()
{struct in_addr addre;addre.s_addr = inet_addr("192.168.1.1");char *buf;buf = inet_ntoa(addre);printf("四点表示法字符串为: %s\n", buf);return 0;
}

服务器端and客户端

TCP执行流程

在这里插入图片描述

UDP执行流程

在这里插入图片描述

socket(创建)
  • 创建通信连接句柄
接口代码
#include <sys/socket.h>int socket(int domain,int type,int protocol);
参数解释
  • domain:指定通信域(也称为地址族),决定了将使用的寻址方案。常见的值包括:

    • AF_INET:IPv4 网络协议。
    • AF_INET6:IPv6 网络协议。
    • AF_UNIXAF_LOCAL:本地通信(同一台机器上的进程间通信)。
  • type:指定套接字的类型,决定了通信方式。常见的值包括:

    • SOCK_STREAM:提供有序的、可靠的、基于连接的字节流服务,通常使用 TCP 协议。

    • SOCK_DGRAM:提供无连接的、不可靠的数据报服务,通常使用 UDP 协议。

    • SOCK_RAW:提供原始网络协议访问,通常用于底层网络编程。

  • protocol:指定具体的协议。大多数情况下,可以设置为 0。

返回值
  • 成功时,返回一个非负整数,即新创建的套接字的文件描述符。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。
实例代码
// 参考下方汇总
bind(绑定)
  • 把电脑上真正的网络地址与一个套接字标识符关联起来
接口代码
#include <sys/types.h>
#include <sys/socket.h>int bind(int sockfd,const struct sockaddr* address,size_t addrlen);
参数解释
  • sockfd: 要绑定的套接字描述符
  • address: 要绑定的地址信息
  • addrlen: address接口体大小
返回值
  • 成功时,返回 0
  • 失败时,返回 -1,并设置 errno 以指示错误原因
实例代码
// 参考下方汇总
listen(监听)
  • 绑定之后,在任何客户端系统可以连接到新建立的服务器端点之前,服务器必须设定为等待连接
接口代码
#include <sys/socket.h>int listen(int sockfd,int backlog);
参数解释
  • sockfd:要设置为监听状态的套接字描述符。
  • backlog:指定等待连接的最大队列长度。这是指尚未被 accept 接受但已经完成三次握手的连接请求的数量
返回值
  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。

实例代码

// 参考下方汇总
accept(接受连接)
  • 当服务器收到客户端connect请求时,必须建立一个全新的套接字来处理这个特定的通信。第一个套接字只用来建立通信,第二个套接字由accept完成
接口代码
#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd,struct sockaddr* address,size_t* add_len);
参数解释
  • sockfd: 处于监听状态的套接字
  • address: 当服务器调用 accept 函数接受一个新的连接请求时,address 会被填充为连接到服务器的客户端的地址信息
  • address: 指定address缓冲区大小,并在返回时更新实际存储长度
返回值
  • 成功时,返回一个新的套接字描述符,该描述符用于与客户端进行通信。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。
实例代码
// 参考下方汇总
connect(请求连接)
  • 客户程序通过一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器
接口代码
#include <sys/socket.h>int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
参数解释
  • sockfd: 指定的套接字将连接到参数address指定服务器套接字
  • addr: 目的地址,IPV4或IPV6
  • addrlen: addr结构体大小

返回值

  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。
实例代码
// 参考下方汇总
recv(接收数据)
  • 从指定的文件描述符读取数据
接口代码
#include <sys/types.h>
#include <sys/socket.h>ssize_t recv(int sockfd,void* buffer,size_t length,int flags);
参数解释
  • sockfd:要从中接收数据的套接字描述符。
  • buf:指向一个缓冲区的指针,用于存储接收到的数据。
  • len:缓冲区的大小(以字节为单位)。
  • flags:指定调用的行为选项。常见的标志包括:
    • 0:默认行为,阻塞直到有数据可读或发生错误。
    • MSG_PEEK:查看传入的数据而不从输入队列中移除。
    • MSG_DONTWAIT:非阻塞模式,如果当前没有数据可读,则立即返回 -1 并设置 errnoEAGAINEWOULDBLOCK
    • MSG_WAITALL:阻塞直到至少 len 字节的数据被接收,或者发生错误
返回值
  • 成功时,返回实际接收到的字节数(可能少于请求的 len)。
  • 如果连接关闭且所有数据已被读取,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。
实例代码
// 参考下方汇总
send(发送数据)
  • 向指定套接字发送数据
接口代码
#include <sys/types.h>
#include <sys/socket.h>ssize_t send(int sockfd,const void* buffer,size_length len,int flags);
参数解释
  • ockfd:要发送数据的套接字描述符。
  • buf:指向包含要发送数据的缓冲区的指针。
  • len:要发送的数据长度(以字节为单位)。
  • flags:指定调用的行为选项。常见的标志包括:
    • 0:默认行为,阻塞直到所有数据被发送或发生错误。
    • MSG_DONTWAIT:非阻塞模式,如果当前无法立即发送数据,则立即返回 -1 并设置 errnoEAGAINEWOULDBLOCK
    • MSG_NOSIGNAL:防止 SIGPIPE 信号,当对端已经关闭连接时,不会产生 SIGPIPE 信号。
    • MSG_OOB:发送带外数据(out-of-band data)。
返回值
  • 成功时,返回实际发送的字节数(可能少于请求的 len)。
  • 如果连接关闭且所有数据已被发送,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。
实例代码
// 参考下方汇总
close(关闭)
  • 通过文件描述符关闭套接字
接口代码
#include <unistd.h>int close(int fd);
参数解释
  • fd:要关闭的文件描述符。
返回值
  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。
实例代码
// 参考下方汇总
recvfrom(接收)
  • UDP接收函数
接口代码
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>ssize_t revcfrom(int sockfd,void* message,size_t length,int flags,struct sockaddr* send_add,socklen_t* add_len);
参数解释
  • sockfd: Socket创建文件描述符
  • message: 存储接收数据
  • length: message缓冲区的最大长度
  • flags: 用于修改revcfrom的行为
    • MSG_PEEK:查看传入的数据,但不将其从输入队列中移除。
    • MSG_WAITALL:等待接收指定数量的字节,除非发生错误或信号中断。
    • MSG_DONTWAIT:使 recvfrom 在非阻塞模式下操作,如果没有数据可读,则立即返回 -1 并设置 errnoEAGAINEWOULDBLOCK
  • send_addr: 存储发送方的地址信息
    • 当为NULL时雨recv工作方式相同
  • add_len: send_addr的实际大小
返回值
  • 成功:返回接收到的字节数(可能小于 length)。
  • 失败:返回 -1,并设置 errno 以指示错误类型。
实例代码
// 参考下方汇总
sendto(发送)
  • UDP发送函数
接口代码
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>ssize_t sendto(int sockfd, const void *message, size_t length, int flags,const struct sockaddr *dest_addr, socklen_t dest_len);
参数解释
  • sockfd: Socket创建文件描述符
  • message: 要发送的信息
  • length: 要发送信息的长度
  • flags: 用于修改sendto
    • MSG_CONFIRM:请求确认。
    • MSG_DONTROUTE:不要路由,直接发送到本地网络。
    • MSG_EOR:记录结束。
    • MSG_OOB:带外数据。
    • MSG_NOSIGNAL:防止 SIGPIPE 信号。
  • dest_addr: 指向发送到目标地址的信息
  • dest_len: dest_addr结构体大小
返回值
  • 成功:返回发送的字节数(可能小于 length)。
  • 失败:返回 -1,并设置 errno 以指示错误类型。
实例代码
// 见下方汇总
综合实例–TCP连接

server-tcp.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>int port = 7777; // 端口号
int main()
{char buf[4096]; // 接收服务器端返回信息缓冲区int socketDes;int addLen; // 接收连接时地址信息长度// 创建套接字文件描述符socketDes = socket(AF_INET, SOCK_STREAM, 0);// 地址信息struct sockaddr_in sin;bzero(&sin, sizeof(sin));sin.sin_family = AF_INET;/*** INADDR_ANY 是一个常量,值为 0x00000000,表示绑定到所有可用的网络接口。* 这意味着服务器将监听所有可用的网络接口上的指定端口*/sin.sin_addr.s_addr = INADDR_ANY;sin.sin_port = htons(port); // 监听端口//  绑定int bindRes;bindRes = bind(socketDes, (struct sockaddr *)&sin, sizeof(sin));if (bindRes == -1){printf("bind--");exit(1);}// 监听int listenRes;listenRes = listen(socketDes, 20);if (listenRes == -1){perror("listen--");exit(1);}while (1){struct sockaddr_in pin;int temp_sock_descriptor;temp_sock_descriptor = accept(socketDes, (struct sockaddr *)&pin, &addLen);// 打印客户端信息printf("Connection accepted from %s:%d\n", inet_ntoa(pin.sin_addr), ntohs(pin.sin_port));if (temp_sock_descriptor == -1){perror("accept--");exit(1);}size_t len;len = recv(temp_sock_descriptor, buf, sizeof(buf), 0);if (len == -1){perror("recv--");exit(1);}printf("喜羊羊: %s\n", buf);// 示例:向客户端发送一条欢迎消息const char *sendMessage = "Welcome to the server!\n";if (strcmp(buf, "你好") == 0)sendMessage = "你也好";send(temp_sock_descriptor, sendMessage, strlen(sendMessage), 0); // 发送数据memset(buf, 0, 4096); // 清空数组close(temp_sock_descriptor); // 关闭套接字}return 0;
}

client-tcp.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>// 服务器地址
const char *hostname = "127.0.0.1";
int port = 7777;int main(int argc, char *argv[])
{char buf[4096];int socketDes;char *message;if (argc < 2){printf("发送信息不能为空\n");exit(1);}message = argv[1];// 创建套接字文件描述符socketDes = socket(AF_INET, SOCK_STREAM, 0);if (socketDes == -1){perror("socket--");exit(1);}// 处理对方IP数据struct hostent *server_host_name;server_host_name = gethostbyname(hostname);if (server_host_name == 0){perror("接受失败--");exit(1);}struct sockaddr_in pin;bzero(&pin, sizeof(pin));pin.sin_family = AF_INET;pin.sin_addr.s_addr = ((struct in_addr *)(server_host_name->h_addr))->s_addr;pin.sin_port = htons(port);// 请求连接int res;res = connect(socketDes, (void *)&pin, sizeof(pin));if (res == -1){perror("res--");exit(1);}// 发送数据ssize_t len;len = send(socketDes, message, sizeof(message), 0);// 接收数据ssize_t reveLen;reveLen = recv(socketDes, buf, sizeof(buf), 0);printf("灰太狼: %s\n", buf);// 关闭close(socketDes);exit(0);
}
综合实力–UDP连接

server-udp.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>int main(int argc, char *argv[])
{int socketDes;socketDes = socket(AF_INET, SOCK_DGRAM, 0);if (socketDes < 0){printf("文件描述符创建失败\n");exit(1);}struct sockaddr_in servAddr;struct sockaddr_in clientAddr;servAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = htonl(INADDR_ANY);servAddr.sin_port = htons(7777);int rc = bind(socketDes, (struct sockaddr *)&servAddr, sizeof(servAddr));if (rc < 0){printf("绑定失败\n");exit(1);}printf("等待接收信息\n");char msg[50];int cliLen, n;while (1){memset(msg, 0x0, 50);cliLen = sizeof(clientAddr);n = recvfrom(socketDes, msg, 50, 0, (struct sockaddr *)&clientAddr, &cliLen);if (n < 0){printf("信息接受失败\n");continue;}printf("%s: from %s:UDP %u : %s\n", argv[0], inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), msg);int sendToRes;char *res = "你也ggg";sendToRes = sendto(socketDes, res, sizeof(res), 0, (struct sockaddr *)&clientAddr, sizeof(clientAddr));if (sendToRes < 0){printf("返回消息失败\n");exit(1);}}exit(0);
}

client-udp.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>int port = 7777;int main(int argc, char *argv[])
{int socketDes;char buf[4096];struct sockaddr_in cliAddr, remoteServAddr;struct hostent *h;if (argc < 3){printf("请输入发送目标地址和发送信息\n");exit(1);}h = gethostbyname(argv[1]);if (h == NULL){printf("%s: 无法识别地址 %s\n", argv[0], argv[1]);exit(1);}remoteServAddr.sin_family = h->h_addrtype;memcpy((char *)&remoteServAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length);remoteServAddr.sin_port = htons(port);// 建立套接字文件描述符socketDes = socket(AF_INET, SOCK_DGRAM, 0);if (socketDes < 0){printf("无法打开文件描述符\n");exit(1);}cliAddr.sin_family = AF_INET;// 客户端可以绑定到任何可用的网络接口cliAddr.sin_addr.s_addr = htonl(INADDR_ANY);// 系统自动分配一个可用的端口cliAddr.sin_port = htons(0);int bindRes;bindRes = bind(socketDes, (struct sockaddr *)&cliAddr, sizeof(cliAddr));if (bindRes < 0){printf("绑定失败\n");return 0;}int sendToRes;for (int i = 2; i < argc; i++){sendToRes = sendto(socketDes, argv[i], strlen(argv[i]) + 1, 0, (struct sockaddr *)&remoteServAddr, sizeof(remoteServAddr));if (sendToRes < 0){printf("发送失败\n");close(socketDes);exit(1);}}char msg[50];int cliLen = sizeof(remoteServAddr);int n = recvfrom(socketDes, msg, 50, 0, (struct sockaddr *)&remoteServAddr, &cliLen);if (n < 0){printf("接收信息失败\n");exit(1);}printf("接收到的数据为:%s\n", msg);exit(0);
}
http://www.lryc.cn/news/446319.html

相关文章:

  • 基于 Web 的工业设备监测系统:非功能性需求与标准化数据访问机制的架构设计
  • 【MySQL】基础入门篇
  • uni-app vue3封装websocket,支持微信小程序
  • 杭州算力小镇:AI泛化解锁新机遇,探寻AI Agent 迭代新路径
  • IT行业的现状与发展趋势
  • 华为认证HCIA篇--网络通信基础
  • 【linux】regulartor-fixed
  • 11年408考研真题解析-计算机网络
  • wireshark使用要点
  • WebGL扩展与WebGPU
  • 基于小安派AiPi-Eyes-Rx的N合1触摸屏游戏
  • Java List sort() 排序
  • Vue.js 与 Flask 或 Django 后端配合
  • 抽奖拼团卷轴模式系统开发小程序源代码解析
  • MySql语言操作数据库---增删改查数据库,表,数据
  • C++深入学习string类成员函数(2):容器管理
  • MariaDB 和 MySQL 全面对比:选择数据库需要考虑这几点
  • Python 实现图形学几何变换算法
  • 接口测试|超详细面试题【附答案】
  • Qt网络编程——QTcpServer和QTcpSocket
  • CentOS 7 aarch64制作openssh 9.9p1 rpm包 —— 筑梦之路
  • Flink和Spark的区别
  • 以太网开发基础-MAC和PHY
  • Java 发布jar包到maven中央仓库(2024年9月保姆级教程)
  • Pandas和Seaborn可视化详解
  • 【Python】Windows下安装使用FFmpeg
  • LLM - 使用 XTuner 指令微调 多模态大语言模型(InternVL2) 教程
  • 【Python】数据可视化之热力图
  • 个人博客系统测试(selenium)
  • 【速成Redis】01 Redis简介及windows上如何安装redis