TCP/UDP 套接字的编写

news/2024/7/25 3:21:36/文章来源:https://blog.csdn.net/choose_heart/article/details/139181791

文章目录

      • 基础知识
    • socket编程
      • UDP套接字编程
      • TCP套接字编写
    • 套接字编写注意事项

基础知识

  • IP地址互联网协议地址(Internet Protocol Address),分配给互联网互联中设备的单一标识,理解成生活中的邮箱地址是比较类似的。在不同的地方入网,就会有不同的ip地址
  • 端口号(port):没操作系统的知识的话,说的粗陋一点,就是如何识别在体态设备上与不同的APP通信,给这些APP分配一个ID,这个ID就叫做端口号。当然前面说法并不完全正确,准确的是端口号应该是区别一台主机中该网络信息向上交付时,是应该交由哪个进程处理而端口号就是用来做这个的。和进程PID有点类似但又有所不同。
    • 一个进程可以有多个port,因为其可以同时收到多个不同的网络消息。
    • 一个port只能对应一个进程,因为他就是用来标识信息向上交付时交给哪个进程的
    • 端口号的范围从0到65535,其中一些知名的端口(0-1023)被预留给标准服务,比如HTTP使用80端口,HTTPS使用443端口,FTP使用21端口等。而动态或私有端口(通常指从1024到65535)则通常是临时分配给应用程序的,尤其是在客户端或不固定服务场景中
  • 网络字节序:就是大小端存储的问题,网络中发送的数据需要考虑大小端,网络规定的标准是大端
    • Big-endian:大端,指一个数据的低字节部分存在高地址处
    • Little-endian:小端,。指一个数据他的低字节处 存储在低地址处。
    • 例子:一个2字节数据:0x11 22 ,加入从左向右地址是增长的,那么大端里面:0x11 22 ;小端:0x22 11
    • 这里说一下我自己容易搞错的点,数据的右边一般才是低字节序,数据的左边一般是高字节序列。你想一个整数,百位肯定是高位把。
    • linux/windows系统都有提供的从主机转网络,网络转主机的函数
    • htonl
    • htons
    • ntohl
    • ntohs

socket编程

socket编程虽然里面需要填写的字段十分多,但是大多数场景下基本类似。
先来整体认识下一些必须要掌握的基础知识
常用接口(windows和linux的函数名都是一样的,但是由于系统不同,其中有些参数类型可能不太一样,以下以WIndows为例,linux你可以使用man + 函数名查看具体情况)
这些常用接口除了类型有一些略微不同,其中许多宏常量,linux和Windows下几乎都是一样的。下面介绍的函数名windows和linux基本都共有。

  • socket
  SOCKET WSAAPI socket([in] int af,   // 常常传入 AF_INET 或者AF_INET6[in] int type,  [in] int protocol);
    • [in]是windows文档的一种习惯,表示这个参数是一个输入形参数
    • af是协议家族, 常常传入 AF_INET 或者AF_INET6。你是ipv4地址就传入AF_INET,ipv6就选AF_INET6
    • type 重点了解,UDP传入SOCK_DGRAM,TCP传入SOCK_STREAM。其余选项可自行取查看手册
    • protocol:如果af,type指定了ipv4/6的协议家族和对应的类型,那么这里传入0由系统给你自动匹配协议是最好。的。如果你传入的是类似SOCK_RAW的值,那这里就需要你特殊指定,这个选项是用来精确控制套接字收到的数据。
  • 返回值
    如果未发生错误, 套接字 将返回引用新套接字的描述符。 否则,将返回值 INVALID_SOCKET,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。

  • bind :一般服务端使用

* int bind([in] SOCKET         s,const sockaddr *addr,[in] int     namelen
);
  • 参数

    • [in] s
      标识未绑定套接字的描述符。
    • addr
      指向要分配给绑定套接字 的本地地址 的 sockaddr 结构的指针。
    • [in] namelen
      addr 指向的值的长度(以字节为单位)。
  • 返回值

  • 如果未发生错误, 绑定 将返回零。 否则,它将返回SOCKET_ERROR,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。

谈到bind,就必须提及Socket编程里面需要的用的结构体sockaddr ,sockaddr_in,sockaddr_in6

  • 使用bind时,一般先创建都应的sockaddr_in(ipv4),sockaddr_in6(ipv6)的。填写其中的家族,端口号,和ip地址。
  • 在强转成 sockaddr* 传入对应的函数bind处理即可。
  • 其中要注意主机大小端和ip地址格式问题。用上述的网络字节序处的函数转变端口号
  • ip地址和和主机之间相互转换的时候,库里面的函数
    如下:

int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
in_addr_t inet_addr(const char *cp);
inet_ntop, inet_pton:这两个是都支持的
下面几个用的相对较少,可以使用时查文档
ntohf
ntohl
ntohll
ntohs

struct sockaddr {ushort  sa_family;char    sa_data[14];
};struct sockaddr_in {short   sin_family;u_short sin_port;struct  in_addr sin_addr;char    sin_zero[8];
};struct sockaddr_in6 {short   sin6_family;u_short sin6_port;u_long  sin6_flowinfo;struct  in6_addr sin6_addr;u_long  sin6_scope_id;
};

上面两个操作TCP和UDP都会用到,但是下面介绍的就是TCP才用了

  • listen :一般是TCP服务端代码使用
int WSAAPI listen([in] SOCKET s,[in] int    backlog
);
  • 参数解释

    • [in] s 标识绑定的未连接的套接字的描述符。
    • [in] backlog 挂起的连接队列的最大长度。
  • 返回值:如果未发生错误, 则侦听 返回零。 否则,将返回 值 SOCKET_ERROR ,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。

  • accept :一般时TCP服务器端使用

SOCKET WSAAPI accept([in]      SOCKET   s,[out]     sockaddr *addr,[in, out] int      *addrlen
);
  • 参数
    [in] s :一个描述符,用于标识已使用 侦 听函数置于侦听状态的套接字。 连接实际上是使用 accept 返回的套接字建立的。
    [out] :addr指向接收连接实体地址的缓冲区的可选指针,该地址称为通信层。 addr 参数的确切格式由创建 sockaddr 结构中的套接字时建立的地址系列确定。
    [in, out] :addrlen指向包含 addr 参数指向的结构长度的整数的可选指针。

  • 返回值
    果未发生错误, 则 accept 将返回 类型为 SOCKET 的值,该值是新套接字的描述符。 此返回值是建立实际连接的套接字的句柄。
    否则,将返回 值 INVALID_SOCKET ,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。

这里返回值拿到的套接字正常读取写入就能和客户端通信了,当成文件描述符使用即可,当然用send和recv来收发数据也可以。

  • connect :一般TCP客户端代码使用
    • 参数
      [in] s 标识未连接的套接字的描述符。
      [in] name 指向应建立连接的 sockaddr 结构的指针。
      [in] namelen name 参数指向的 sockaddr 结构的长度(以字节为单位)。

    • 返回值
      如果未发生错误, 则连接 返回零。 否则,它将返回SOCKET_ERROR,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。

链接成功后就可以使用,当成文件描述符来使用也是没有问题的。当然用send和recv来收发数据也可以

UDP套接字编程

UDP套接字就只需要两个操作,socket和bind,做完之后UDP程序就能通信了。
具体步骤
服务器

  • 先申请一个套接字,填入对应的sockaddr的字段
  • 再bind 改套接字即可
  • 再用sendto 和 recv收发数据

客户端:

  • 创建一个套接字
  • 用sendto 和recv发收数据即可
    Server
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>#pragma comment(lib, "ws2_32.lib")int main() {//使用windows网络库需要做的,linux下就没这过程WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {std::cerr << "Failed to initialize winsock." << std::endl;return -1;}SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (serverSocket == INVALID_SOCKET) {std::cerr << "Failed to create socket." << std::endl;WSACleanup();return -1;}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(12345); // 服务器监听端口serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {std::cerr << "Failed to bind socket." << std::endl;closesocket(serverSocket);WSACleanup();return -1;}std::cout << "Server is listening on port 12345..." << std::endl;char buffer[1024];sockaddr_in clientAddr;int addrLen = sizeof(clientAddr);while (true) {int bytesReceived = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (SOCKADDR*)&clientAddr, &addrLen);if (bytesReceived > 0) {buffer[bytesReceived] = '\0';std::cout << "Message from client: " << buffer << std::endl;// Echo back the message to the clientsendto(serverSocket, buffer, bytesReceived, 0, (SOCKADDR*)&clientAddr, sizeof(clientAddr));}}closesocket(serverSocket);WSACleanup();return 0;
}

Client


#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>#pragma comment(lib, "ws2_32.lib")int main() {WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {std::cerr << "Failed to initialize winsock." << std::endl;return -1;}SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (clientSocket == INVALID_SOCKET) {std::cerr << "Failed to create socket." << std::endl;WSACleanup();return -1;}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(12345); // 服务器端口号inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr); // 服务器IP地址,这里假设为本地环回地址std::string message;std::cout << "Enter a message to send to the server: ";std::getline(std::cin, message);if (sendto(clientSocket, message.c_str(), message.length(), 0, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {std::cerr << "Failed to send message." << std::endl;} else {std::cout << "Message sent to server." << std::endl;}char buffer[1024];int bytesReceived = recvfrom(clientSocket, buffer, sizeof(buffer), 0, NULL, NULL);if (bytesReceived > 0) {buffer[bytesReceived] = '\0';std::cout << "Reply from server: " << buffer << std::endl;}closesocket(clientSocket);WSACleanup();return 0;
}

TCP套接字编写

服务端:

  • socket获取一个套接字
  • bind这个套接字
  • 监听这个套接字
  • accept,接受链接成功返回一个套接字,用该套接字通信即可。用send和recv进行通信即可。可以接受多个套接字

客户端:

  • socket 获取一个套接字
  • connect 对应的服务器,成功后用该套接字 send和recv即可。

linux下一切皆文件,TCP这里读取内容的时候,其实用read和write来收发数据也是没有问题的。

服务器代码

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>#pragma comment(lib, "ws2_32.lib")int main() {WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {printf("WSAStartup failed: %d\n", GetLastError());return 1;}SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (ListenSocket == INVALID_SOCKET) {printf("Error at socket(): %ld\n", WSAGetLastError());WSACleanup();return 1;}sockaddr_in service;service.sin_family = AF_INET;service.sin_addr.s_addr = inet_addr("127.0.0.1"); // 或者使用INADDR_ANY监听所有地址service.sin_port = htons(27015); // 自定义端口号if (bind(ListenSocket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) {printf("bind failed with error: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;}if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {printf("listen failed with error: %ld\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;}printf("Listening on port 27015...\n");SOCKET ClientSocket;while ((ClientSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET) {char recvbuf[512];int iResult = recv(ClientSocket, recvbuf, 512, 0);if (iResult > 0)printf("Received: %s\n", recvbuf);else if (iResult == 0)printf("Connection closing...\n");elseprintf("recv failed: %d\n", WSAGetLastError());// 发送响应(可选)char *response = "Hello from server!";send(ClientSocket, response, strlen(response), 0);closesocket(ClientSocket);}if (ClientSocket == INVALID_SOCKET) {printf("accept failed: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;}closesocket(ListenSocket);WSACleanup();return 0;
}
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>#pragma comment(lib, "ws2_32.lib")int main() {WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {printf("WSAStartup failed: %d\n", GetLastError());return 1;}SOCKET ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (ConnectSocket == INVALID_SOCKET) {printf("Error at socket(): %ld\n", WSAGetLastError());WSACleanup();return 1;}sockaddr_in serverAddress;serverAddress.sin_family = AF_INET;serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP地址serverAddress.sin_port = htons(27015); // 与服务器端相同的端口号if (connect(ConnectSocket, (SOCKADDR*)&serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {printf("connect failed: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;}char sendbuf[] = "Hello from client!";int iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);if (iResult == SOCKET_ERROR) {printf("send failed: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;}char recvbuf[512];iResult = recv(ConnectSocket, recvbuf, 512, 0);if (iResult > 0)printf("Received: %s\n", recvbuf);else if (iResult == 0)printf("Connection closed\n");elseprintf("recv failed: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 0;
}

套接字编写注意事项

  • TCP和UDP都是全双工的,不用担心收发数据互相干扰的问题
  • 为了收发不互相干扰,一般要采取并发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1052460.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构——红黑树

红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但是在每个结点上会多出一个存储位来表示结点的颜色&#xff0c;可以是红色(RED)或者黑色(BLACK),通过任何一条根到叶子的路径上各个结点着色方式的限制&#xff0c;确保红黑树没有一条路径会比其他路径长出两倍&…

前端开发工程师——数据可视化

canvas canvas绘制线段 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthd…

Web安全:SQL注入之时间盲注原理+步骤+实战操作

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

Golang | Leetcode Golang题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; func connect(root *Node) *Node {if root nil {return root}// 每次循环从该层的最左侧节点开始for leftmost : root; leftmost.Left ! nil; leftmost leftmost.Left {// 通过 Next 遍历这一层节点&#xff0c;为下一层的节点更新 Next …

帝国CMS验证码不显示怎么回事呢?

帝国CMS验证码有时候会不显示或打叉&#xff0c;总结自己的解决方法。 1、检查服务器是否开启GD库 测试GD库是否开启的方法&#xff1a;浏览器访问&#xff1a;/e/showkey/index.php&#xff0c;如果出现一堆乱码或报错&#xff0c;证明GD库没有开启&#xff0c;开启即可。 2…

深度学习-转置卷积

转置卷积 转置卷积&#xff08;Transposed Convolution&#xff09;&#xff0c;也被称为反卷积&#xff08;Deconvolution&#xff09;&#xff0c;是深度学习中的一种操作&#xff0c;特别是在卷积神经网络&#xff08;CNN&#xff09;中。它可以将一个低维度的特征图&#x…

echarts学习篇

一、使用echarts 1.引入 Apache ECharts <!DOCTYPE html> <html> <head> <meta charset"utf-8" /> <!-- 引入刚刚下载的 ECharts 文件 --> <script src"echarts.js"></script> </head> </html> 2.…

数据结构---链表

1、链表分类 对于链表可以分为几种&#xff0c;不同的分类对应不同的应用场景&#xff0c;其中&#xff0c;双向循环链表和单向链表最常用。 1.1链表按照有头无头分类 也就是说有无哨兵位&#xff0c;哨兵位&#xff0c;是一个开辟的空间&#xff0c;但是不放置数据&#xf…

AURIX TC3xx单片机介绍-启动过程介绍1

从各个域控制器硬件解决方案来看,MPU可能来自多个供应商,有瑞萨,有NXP等,但对于MCU来说,基本都采用英飞凌TC3xx。 今天我们就来看一下TC3xx的启动过程,主要包含如下内容: uC上电过程中,会经过一个上电时序,从复位状态“脱离”出来;Boot Firmware是复位后第一个执行的…

金职优学:分析央国企面试如何通关?

在当今竞争激烈的就业市场中&#xff0c;中央和国有企业&#xff08;以下简称“央国企”&#xff09;的面试机会对求职者来说是非常有吸引力的。这些企业通常拥有稳定的发展前景、良好的薪酬福利和广阔的职业发展空间。但是&#xff0c;要想成功通过央国企的面试&#xff0c;求…

YOLOv8+PyQt5西红柿成熟度检测系统完整资源集合(yolov8模型,从图像、视频和摄像头三种路径识别检测,包含登陆页面、注册页面和检测页面)

西红柿成熟度检测&#xff08;https://mbd.pub/o/bread/mbd-ZpWbk5ly&#xff09;_哔哩哔哩_bilibili 资源包含可视化的西红柿成熟度检测系统&#xff0c;基于最新的YOLOv8训练的西红柿成熟度检测模型&#xff0c;和基于PyQt5制作的可视化西红柿成熟度检测系统&#xff0c;包含…

加速模型训练 GPU cudnn

GPU的使用 在定义模型时&#xff0c;如果没有特定的GPU设置&#xff0c;会使用 torch.nn.DataParallel 将模型并行化&#xff0c;充分利用多GPU的性能&#xff0c;这在加速训练上有显著影响。 model torch.nn.DataParallel(model).cuda() cudnn 的配置&#xff1a; cudnn.…

Habicht定理中有关子结式命题3.4.6的证明

个人认为红色区域有问题&#xff0c;因为 deg ⁡ ( ϕ ( S j ) ) r \deg{\left( \phi\left( S_{j} \right) \right) r} deg(ϕ(Sj​))r&#xff0c;当 i ≥ r i \geq r i≥r时&#xff0c; s u b r e s i ( ϕ ( S j 1 ) , ϕ ( S j ) ) subres_{i}\left( \phi(S_{j 1}),\p…

景源畅信电商:做抖店的成本高吗?

在当今数字化时代&#xff0c;抖音不仅仅是一个分享短视频的平台&#xff0c;更是一个充满商机的市场。随着抖音用户量的激增&#xff0c;越来越多的商家和个人开始关注在抖音上开设店铺&#xff0c;即所谓的“抖店”。那么&#xff0c;做抖店的成本高吗?这个问题困扰着许多初…

《python编程从入门到实践》day40

# 昨日知识点回顾 编辑条目及创建用户账户 暂没能解决bug&#xff1a; The view learning_logs.views.edit_entry didnt return an HttpResponse object. It returned None instead.# 今日知识点学习 19.2.5 注销 提供让用户注销的途径 1.在base.html中添加注销链接 …

数据仓库ETL

小白的数据仓库学习笔记 2024/5/20 18:25 文章目录 ETLdim打开创建项目&#xff08;选这个&#xff0c;这个是做etl的&#xff09;建立元数据的连接同样的&#xff0c;建立与数据仓库的连接新建ssis包序列容器全量etl增量etl建立sql任务双击打开&#xff0c;设置连接、内容 双击…

linux 阿里云服务器安装ImageMagick和php扩展imagick

操作系统版本 Alibaba Cloud Linux 3.2104 LTS 64位 # 1.安装ImageMagick yum install -y ImageMagick ImageMagick-devel # 没有pecl要先安装pecl 和头文件 sudo yum install php-devel # 2.pecl 安装扩展 pecl install imagick #寻找所有php.ini文件 find / -name php.…

Word整理论文参考文献

1.安装Zotero软件 2.安装Zotero的Chrome网站插件&#xff0c;并将插件固定到浏览器 3.安装Word的Zotero插件 4.在DBLP网站https://dblp.org/search 搜索需要添加的参考文献->点击BibTex->点击网页右上角的Zotero符号&#xff08;即第二步所指的符号&#xff09;->至…

《python本机环境多版本切换》-两种方式以及具体使用--venv/pyenv+pycharm测试

阿丹&#xff1a; source myenv/bin/activate 在开发使用rasa的时候发现自己安装的python环境是3.12的&#xff0c;和rasa不兼容&#xff0c;所以实践一下更换多python环境。 使用虚拟环境 在Python中使用虚拟环境来切换Python版本是一个常见的做法&#xff0c;这可以帮助你…

创新实训2024.05.28日志:记忆化机制、基于MTPE与CoT技术的混合LLM对话机制

1. 带有记忆的会话 1.1. 查询会话历史记录 在利用大模型自身能力进行对话与解答时&#xff0c;最好对用户当前会话的历史记录进行还原&#xff0c;大模型能够更好地联系上下文进行解答。 在langchain chat chat的chat函数中&#xff0c;通过实现langchain框架提供的ChatMemo…