Linux学习笔记(Socket)

Linux-Socket

  • 1、基础知识
  • 2、服务端
  • 3、客户端
  • 4、读写操作
    • 4.1、读写函数
    • 4.2、阻塞IO和非阻塞IO
  • 5、例程

1、基础知识

socket用于计算机之间的网络通信,无论是构建服务器还是客户端,我们仅需要三个信息,服务器的ip地址,对应进程的端口号,通信协议
拿到ip地址,便自然知道其ip种类且同时知道该服务器的位置,拿到端口号便知道具体和哪个程序对接。一般而言我们使用ipv4的地址格式,端口号也一般设置得大一些,以防止和服务器原本的应用程序冲突。通信协议则分为流式和报式两个种,它们的代表协议分别是TCP和UDP。
总而言之,只要得到这些信息,服务器就能构建其服务程序,客户端也能准确的找到对应的服务程序。
而socket这个名字,也生动的表达的这样一层意思,把服务器看成一个插排,客户端看成各类用电器,一个插排可以给N个设备供电。
在这里插入图片描述

2、服务端

第一步,创建一个套接字,并拿到一个文件描述符,以便捷的表示这个套接字。
第二步,构建一个结构体,让这个结构体绑定各种信息(ip地址,端口号,通信协议)。
第三步,开始准备接受客户端的连接请求。
第四步,处理客户端的连接请求(验证ip地址和端口号是否对上),并为客户端分配独一无二的文件描述符。
最后,根据需要进行读写操作。
下面的例程,每一步具体代码怎么写我大致有标注一下。

//设置ip和端口号
std::string server_ip_address = "xxx.xxx.xxx.xxx";
int server_port = 8888;//step 1
int fd = socket(AF_INET, SOCK_STREAM, 0);//step2
sockaddr_in addr;		//定义一个结构体
addr.sin_family = AF_INET;		//指定ip地址格式
addr.sin_port = htons(server_port);		//设置端口
inet_pton(AF_INET, server_ip_address.c_str(),  &addr.sin_addr.s_addr);	//设置ip地址
bind(fd, (struct sockaddr*)&addr, sizeof(addr));	//绑定信息//step3
listen(fd, 128);//step4
sockaddr_in client_addr;		//定义一个结构体接受客户端信息
socklen_t clilen = sizeof(client_addr);
int cfd = accept(sfd, (struct sockaddr*)&client_addr, &clilen);		//比对信息是否匹配

如果想要具体了解各个函数的参数的意义,直接copy之后搜索即可。
有几个点可以注意一下
1、写代码的时候数据的存储方式是小端存储,即主机字节序。譬如这一句:

int server_port = 8888;

它在内存当中就是主机存储。但是用于网络通讯的时候,又是大端存储(又称网络字节序)。所以需要转化一下,一般用到的函数有:htons, htonl, ntohs, ntohl.
意思也很直白,以htons为例,htons即host to network short。host意为主机字节序,network意为网络字节序,short指的是要转化的字节数,short指的是2个字节,long指的是4个字节(和long int对应的字节数不太一样)。
2、listen(fd, 128);这里的128并不是指最多只能接受128个客户端,而是在listen那一瞬间最多只能处理128个,可以多执行几次listen的。
3、在字符串传输的时候不需要考虑字节序的问题。因为一个字符本身就占一个字节,不存在字节内部被打乱的可能。也就是说,所谓字节序的问题是存在局部的,而不存在于整体中。
举个例子,9这个整数,占四个字节,转化成字节流是长这样的。
小端存储(主机字节序):9:0x00 0x00 0x00 0x09
大端存储(网络字节序):9:0x09 0x00 0x00 0x00(其实比对一下,就可以get到为啥传输的时候变成大端的了)
如果是3和9两个数字分别发过去,则不会,也不可以影响3和9的发送顺序。所以对于字符串"abcd",它的发送顺序就是"a" “b” “c” “d”。又因为它们本身就只占一个字节,自然不会被存储方式干扰。
但是如果一个汉字,它由于占多个字节,倒有可能受到字节序的影响。
总的来说,字节序的问题只出现在一个整体被拆分成N个字节,然后传输之后重组才可能出现。
4、如果cfd不为-1则表示正确和客户端建立连接,可以进行读写操作了。具体代码之后再说。

3、客户端

从客户端的角度来说,其主要任务就是拿到服务器的ip地址,端口号,然后主要去和客户端建立起连接。譬如插线要去匹配插座一样。核心步骤和构建服务器的步骤大同小异,大概如下:
第一步,创建一个套接字,并拿到一个文件描述符,以便捷的表示这个套接字。
第二步,构建一个结构体,让这个结构体绑定服务器的各种信息ip地址,端口号,通信协议)。
第三步,利用以上这些信息尝试去连接服务器,如果构建成功则返回的整形变量是正值。
第四步,根据任务需要进行读写操作。

//step 1:
int fd = socket(AF_INET, SOCK_STREAM, 0);//step 2:
sockaddr_in saddr;
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
inet_pton(AF_INET, server_ip_address.c_str(),  &addr.sin_addr.s_addr);//step 3:
do{		ret = connect(sfd, (struct sockaddr*)&saddr, sizeof(saddr));	//connect是一个非阻塞函数,所以需要用while不断尝试去建立连接std::cout << "try to connect server...\n";sleep(1);
}while(ret == -1);

4、读写操作

4.1、读写函数

读用的是write

ssize_t write(int fd, const void *buf, size_t count);
  • fd是文件描述符。
  • buf被写内容的地址。
  • count自然是被写内容的字节数。
  • 返回值:如果写入成功,则返回写入的字节数目。如果失败,返回-1;如果正常写入,但没写入任何东西,则返回0。

读操作用的是read函数

ssize_t read(int fd, void *buf, size_t count);
  • fd:文件描述符。
  • buf:指向读取的内容的指针 。
  • count:要读取的字节数 。
  • 返回值:如果读取成功,则返回读取的字节数目。如果失败,返回-1;如果正常读取,但没读取到东西,返回0。

其实这里write和read和Linux文件IO操作中的read和write函数是一样的,包括close操作也是。可以看我之前写的文章。
链接: Linux学习笔记之四(文件IO、目录IO)

4.2、阻塞IO和非阻塞IO

其实无论是调用write函数还是read函数,都是在内核空间中的缓存区进行操作。流程大概是,通过write函数把数据写到内核空间的写缓存区,然后内核自动通过一些协议把数据传输到读缓存区,然后read函数才能从读缓存区把数据给读出来。
在这里插入图片描述
但是这个读写操作分为阻塞式读写和非阻塞式读写,专业术语叫阻塞IO和非阻塞IO。默认情况下是采用阻塞式IO,在阻值式模式下,write操作如果发现写缓存区已经满了,则会一直在那里等待,直到有多余空间可以写入;对于read操作来说,则是如果发现读缓存区没有数据,则一直阻塞等待,直到有数据出现。
而非阻塞式刚好相反,以read为例,如果发现读缓存区中没有数据,则返回-1,然后继续执行其它任务。
非阻塞可以通过相关函数来设置,自行上网查即可。下面讲讲它们的优缺点。

  • 阻塞式IO
    • 优点:操作和逻辑都比较简单,确保write和read能正确执行才继续执行其它任务。
    • 缺点:浪费线程时间,有可以阻塞的时间过长而影响其它任务的执行。
  • 非阻塞式IO
    • 优点:可以及时的去执行其它任务,提高线程的利用率。
    • 缺点:可以导致读写数据不及时。

总的两说,两者各有优劣。但也有一些方法可以取长补短,比如select,epoll,poll等。暂且不谈了。

5、例程

服务器例程,我将读写分为两个线程来写。

#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>std::string server_ip_address = "192.168.52.130";
int server_port = 8888;
int exit_flag = 0;
std::mutex mu;int ServerInit()
{//create a socket descriptionint fd = socket(AF_INET, SOCK_STREAM, 0);sockaddr_in addr;//get ip typeaddr.sin_family = AF_INET;//get host portaddr.sin_port = htons(server_port);//get ip iddressinet_pton(AF_INET, server_ip_address.c_str(),  &addr.sin_addr.s_addr);//link above information to socketbind(fd, (struct sockaddr*)&addr, sizeof(addr));return fd;
}void SendThread(int cfd)
{std::string meg;while(true){std::getline(std::cin, meg);if(meg == "exit"){write(cfd, meg.c_str(), meg.length());mu.lock();exit_flag = 1;mu.unlock();break;} write(cfd, meg.c_str(), meg.length());}
}void ReceThread(int cfd)
{char buf[1024];while(exit_flag == 0){//set buff to zeromemset(buf, 0, sizeof(buf));int len = read(cfd, buf, sizeof(buf));if(len > 0)     std::cout << " receive from client is: " << buf << std::endl;}
}int main()
{//sfd: server file descriptionint sfd = ServerInit();//beign to wait for client, block.listen(sfd, 128);sockaddr_in client_addr;socklen_t clilen = sizeof(client_addr);//receive the information from client, cfd: client file desriptionint cfd = accept(sfd, (struct sockaddr*)&client_addr, &clilen);if(cfd != -1){std::thread send_thread(SendThread, cfd);std::thread rece_thread(ReceThread, cfd);send_thread.join();rece_thread.join();}close(cfd);close(sfd);return 0;
}

客户端例程

#include <iostream>
#include <stdio.h>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>std::string server_ip_address = "192.168.52.130";
int server_port = 8888;int ClientInit(sockaddr_in& addr)
{//create a socket descriptionint fd = socket(AF_INET, SOCK_STREAM, 0);//get ip typeaddr.sin_family = AF_INET;//get host portaddr.sin_port = htons(server_port);//get ip iddressinet_pton(AF_INET, server_ip_address.c_str(),  &addr.sin_addr.s_addr);return fd;
}int main()
{//sfd: server file description, saddr: server addresssockaddr_in saddr;int sfd = ClientInit(saddr);int ret;do{ret = connect(sfd, (struct sockaddr*)&saddr, sizeof(saddr));std::cout << "try to connect server...\n";sleep(1);}while(ret == -1);std::cout << "connect server successfully!\n";while(1){static char buf[1024] = {0};static std::string str = "hello, i am client.";write(sfd, str.c_str(), str.size());int len  = read(sfd, buf, sizeof(buf));if(len > 0){std::cout << "client receive is: " << buf << std::endl;memset(buf, 0 , sizeof(buf));} else if(len == 0){std::cerr << "lose connection with server.\n";break;}}close(sfd);return 0;
}

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

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

相关文章

OpenAI 新发布的 GPT-4o,有血有肉的Ai来了,可实时语音视频交互

今天&#xff0c;OpenAI又又又开发布会了。 在大众心里&#xff0c;现在也基本上都知道&#xff0c;奥特曼是一个贼能PR的人。 每一次的PR的时间点&#xff0c;都拿捏的极其到位&#xff0c;精准的狙击其他厂商。比如说上一次Sora&#xff0c;其实你会发现从头到尾就是一个PR的…

奥维地图下载高清影像的两种方式!以及ArcGIS、QGIS、GlobalMapper、自编工具下载高清影像的方法推荐!

今天来介绍一下奥维互动地图是如何下载高清影像的&#xff0c;也不是多了不起的功能&#xff01;有朋友问&#xff0c;加上这个软件确实用的人多。 下载的高清数据在ArcGIS中打开的效果&#xff01; 开始介绍奥维之前我们也介绍一下我们之前介绍的几个方法&#xff0c;没有优劣…

zabbix触发器配置定期生效教程

在企业生产过程中&#xff0c;并非所有的设备都需要全天候、满负载运转&#xff0c;也有些仅需要周期性的运转即可。例如&#xff0c;在某家企业&#xff0c;有一批这样的机器&#xff0c;每天都会在固定的时间跑批量任务&#xff0c;期间&#xff0c;机器的CPU使用率会有明显的…

Pytorch学习-利用Dataset类定义自己的数据集

定义自己的数据集类需要继承torch.utils.data中的Dataset类 主要实现两个方法&#xff0c;即__len__和__getitem__ from torch.utils.data import Dataset class VOCDataSet(Dataset):#初始化def __init__(self):pass#返回数的长度def __len__(self):pass#返回样本和标签def …

LeetCode 126题:单词接龙 II

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

CMake配置安装gdal3.6.2库

安装GDAL 3.6.2库可以通过使用CMake来完成&#xff0c;尤其是在Windows平台上。这里我将提供一个详细的步骤指南&#xff0c;包括如何配置CMake以及如何使用它来构建和安装GDAL。前提是你已经安装了CMake和相应的编译器&#xff08;如Visual Studio或GCC&#xff09;。 ### 步骤…

联软安渡 UniNXG 安全数据交换系统 任意文件读取漏洞复现

0x01 产品简介 联软安渡UniNXG安全数据交换系统,是联软科技自研的业内融合网闸、网盘和DLP的一体机产品,它同时支持多网交换,查杀毒、审计审批、敏感内容识别等功能,是解决用户网络隔离、网间及网内数据传输、交换、共享/分享、存储的理想安全设备,具有开创性意义。 UniN…

什么是BI看板?选择BI看板制作工具时一定要考虑这些方面

BI看板也称为商业智能仪表板&#xff0c;是一种直观的数据可视化工具&#xff0c;它将关键业务指标&#xff08;KPIs&#xff09;和数据以图表、图形和表格的形式集中展示&#xff0c;使用户能够快速获取企业运营的实时概览。 这种数据可视化方式不仅使得复杂的数据信息易于理…

Python图嵌入信息潜在表征算法

&#x1f4dc;用例 &#x1f4dc;Python社群纽带关系谱和图神经 | &#x1f4dc;C和Python通信引文道路社评电商大规模行为图结构数据模型 | &#x1f4dc;角色图嵌入学习 | &#x1f4dc;图全局结构信息学习 | &#x1f4dc;图编码解码半监督学习 | &#x1f4dc;富文本表征学…

FPGA - Xilinx系列高速收发器---GTX

1&#xff0c;GTX是什么&#xff1f; GT &#xff1a;Gigabit Transceiver千兆比特收发器&#xff1b; GTX &#xff1a;Xilinx 7系列FPGA的高速串行收发器&#xff0c;硬核 xilinx的7系列FPGA根据不同的器件类型&#xff0c;集成了GTP、GTX、GTH、GTZ四种串行高速收发器&am…

(python)cryptography-安全的加密

前言 cryptography 是一个广泛使用的 Python 加密库&#xff0c;提供了各种加密、哈希和签名算法的实现。它支持多种加密算法&#xff0c;如 AES、RSA、ECC 等&#xff0c;以及哈希函数&#xff08;如 SHA-256、SHA-384 等&#xff09;和数字签名算法(如 DSA、ECDSA 等). 目录 …

pikachu靶场通关之csrf漏洞通关教程

目录 CSRF&#xff08;get型&#xff09; 1.打开网站&#xff0c;点击右上角提示 2.登录之后&#xff0c;点击修改个人信息 3.修改上述内容&#xff0c;打开抓包工具 4.抓到修改用户信息的数据包 5.构造虚假url&#xff0c;诱导用户点击 6.弹到修改后的界面 ​编辑 7.返…

前端已死? Bootstrap--CSS组件

目录 Bootstrap 下载 Bootstrap--全局CSS样式 栅格系统 栅格参数 正常显示 实例 代码演示: 排版 代码演示 表格 代码演示 表单 代码演示 等等...(文档很清晰了) Bootstrap--组件 结合演示:(页面) Bootstrap Bootstrap v3 中文文档 Bootstrap 是最受欢迎的 HT…

计算机视觉与深度学习实战:以Python为工具,基于小波的图像压缩技术

一、引言 在数字图像处理领域,图像压缩技术一直是一个热门且重要的话题。随着计算机视觉和深度学习技术的快速发展,图像压缩技术也在不断地进步和创新。本文将探讨如何使用Python这一强大的编程工具,结合深度学习技术和小波变换,实现高效的图像压缩。 二、计算机视觉与深度…

Ubuntu22.04 sudo pip 找不到命令解决方法

解决方法&#xff1a; &#xff08;1&#xff09;编辑/etc/sudoers&#xff1a; sudo vim /etc/sudoers 将Defaults env_reset修改为&#xff1a; Defaults !env_reset &#xff08;2&#xff09;编辑配置文件~/.bashrc&#xff1a; sudo vim ~/.bashrc 添加&#xff1a; a…

ps学习计划

将学习完成以下功能 1.更换证件照背景色 2.人像换装 3.人像去水印文字 4. P掉其中的一个人 去除无关的人 5.生活照扶正 6.瘦身 美白 去下巴 单眼皮变双眼皮 7.头发变颜色 8.竖图变横图技巧 9.帮小姐姐P裙子 10.人物头发变黑技巧 11.抠印章 一键抠图 12.抠图换背景 …

测试计划和管理:构建高质量软件的关键

目录 测试原则 测试计划 概述 过程 目标 策略 制定计划 范围分析和工作量估计 测试资源要求和进度管理 测试风险的控制 测试报告 测试管理工具 结论 测试是软件开发过程中的关键组成部分&#xff0c;确保了最终产品的质量和可靠性。有效的测试计划和管理对于确保测…

建筑乙级资质换证过程中的质量与安全保障

在建筑乙级资质换证过程中&#xff0c;确保质量与安全保障是至关重要的&#xff0c;它不仅关系到企业的声誉和长期发展&#xff0c;也直接关系到工程项目的成功实施和客户的满意度。以下是在换证过程中加强质量与安全保障的建议&#xff1a; 1. 明确质量与安全管理目标 在换证之…

Unreal Engine 4 简介

Unreal Engine 4&#xff08;虚幻引擎4&#xff09;是由Epic Games开发的一款强大而全面的游戏引擎。以下是关于Unreal Engine 4的简介&#xff1a; 开发目的&#xff1a;Unreal Engine 4最初是为了开发第一人称射击游戏而设计的&#xff0c;但随着时间的推移&#xff0c;它已…