【Linux】Linux下使用套接字进行网络编程

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

用于网络应用开发,使用系统提供的一套API函数接口,称为套接字函数

所有的I/O设备都被抽象为文件,一切皆文件,Everything is a File,磁盘、网络数据、终端,甚至进程间通信工具管道pipe等都被当做文件对待。

套接字文件描述符包含有IP地址和PORT端口号。

socket创建套接字

函数原型:int sockfd=socket(AF_INET,SOCKET_STREAM(流式or报式),int protocol(默认流式协议TCP))

成功返回sock_fd,失败返回-1,并设置errno

bind绑定IP和端口

函数原型:bind(sock_fd,struct sockaddr *addr)

struct sockaddr *addr是早期的网络通信结构体,为了向前兼容,仍然保留。

struct sockaddr_in addr网络信息结构体

addr.sin_family=AF_INET
addr.sin_port=大端(8080)
addr.sin_addr.s_addr=大端(ip)

大小端转换常用函数

htons()小端转大端端口

htonl()小端转大端IP

ntohs()大端转小端端口

ntohl()大端转小端IP

inet_pton(AF_INET,char *ip,void *addr)

inet_ntop(AF_INET,void *addr,char *ip,16)


绑定可以对socket设置自定义的IP和端口号,其次当绑定某端口的进程退出,可以临时禁用端口号,禁用时长为2MSL。

成功返回0,失败返回-1

listen网络事件监听(TCP)

函数原型:listen(int sockfd,int backlog)

backlog:监听序列数

成功返回0,错误返回-1,并设置errno,用于错误处理

connet请求TCP连接函数(主动端)

函数原型:connect(int mysockfd,struct sockaddr * dest,socklen_t addrlen)

客户端调用该函数向服务器发起TCP连接请求

成功返回0,错误返回-1,并设置errno,用于错误处理

Accept等待TCP连接请求,完成连接(被动端)

Accept为阻塞等待连接,每次只能连接一个客户端,如果要多次连接,要执行多次该函数

函数原型:

int client_fd=accpet(int server_fd,sockaddr * clientAddr(output),socklen_t *Addrlen(intput|output))

arg[1] 包含自身网络信息的socket

arg[2] 连接成功,传出保存客户端的网络信息(IP,port)

arg[3] 传入可以接收的网络信息大小,传出实际大小

成功返回请求端的socketfd,错误返回-1,并设置errno,用于错误处理

CS架构中,服务器socket只有一个,客户端socket有多个(server_fd用于连接,client_fd用于与客户端交互数据)

recv读取数据

在Linux下,read也可以读取socket数据(TCP),因为在Linux下,套接字以文件描述符的形式记录。

函数原型:ssize_t recv(int sockfd,void *buf,size_t size,int flag)

成功返回读取的数据量,失败返回-1

可以通过将flag设置为MSG_DONTWAIT实现非阻塞读取数据

send发送数据

在Linux下,write也可以发送socket数据(TCP),因为在Linux下,套接字以文件描述符的形式记录。

函数原型:ssize_t send(int sockfd,void *buf,size_t len,int flag)

面试题:

在编写一个服务端-客户端模型时,客户端异常退出,服务端也异常退出。

原因

当客户端异常退出时,客户端关闭了它的读端文件描述符。此时,如果服务端尝试向管道或套接字写数据,会触发 SIGPIPE 信号,默认行为是终止进程,因此服务端也会异常退出。

解决方案

可以在 send() 函数中使用 MSG_NOSIGNAL 标志位,来忽略 SIGPIPE 信号,从而避免服务端异常退出

send(int sockfd, buffer, len, MSG_NOSIGNAL);

使用 MSG_NOSIGNAL 标志位,写操作在对端关闭连接的情况下不会触发 SIGPIPE 信号,而是会返回 EPIPE 错误,程序可以根据这个错误码进行处理而不是直接终止。

在公网上利用TCP协议实现网络数据传输

由于服务端和客户端不在同一个局域网下,所以需要通过互联网进行通信,虚拟机要配置网络连接。

服务端:

服务端是运行在公网上的阿里云服务器。

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>#define _SERVER_IP "xxx.xxx.xxx.xxx"
#define _PORT 8080
#define _BACKLOG 128
#define _SHUTDOWN 1
#define _TRUE 1
#define _FALSE 0
#define _IPSIZE 16
#define _RECVLEN 1500int main()
{struct sockaddr_in serverAddr,clientAddr;int server_fd;int client_fd;char Result[_RECVLEN];char client_ip[_IPSIZE];socklen_t Addrlen;serverAddr.sin_family=AF_INET;serverAddr.sin_port=htons(_PORT);serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);server_fd=socket(AF_INET,SOCK_STREAM,0);bind(server_fd,(struct sockaddr*)&serverAddr,sizeof(serverAddr));listen(server_fd,_BACKLOG);printf("Test TCP Server Version 1.1.0 is Running...\n");while(_SHUTDOWN){Addrlen=sizeof(clientAddr);if((client_fd=accept(server_fd,(struct sockaddr*)&clientAddr,&Addrlen))>0){bzero(Result,sizeof(Result));bzero(client_ip,sizeof(client_ip));inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,client_ip,_IPSIZE);printf("Connection From :IP[%s],PORT[%d]\n",client_ip,ntohs(clientAddr.sin_port));sprintf(Result,"Hi [%s] Welcome to my TCP test server!service version 1.1.0...",client_ip);send(client_fd,Result,strlen(Result),0);close(client_fd);}else{perror("accpet failed");close(server_fd);exit(0);}}close(server_fd);return 0;
}

客户端:

客户端是运行在本地上的虚拟机镜像

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>//客户端源码编写,连接服务器成功,服务器反馈信息#define _IP "xxx.xxx.xxx.xxx"
#define _PORT 8080
int main()
{struct sockaddr_in ServerAddr;bzero(&ServerAddr,sizeof(ServerAddr));ServerAddr.sin_family=AF_INET;ServerAddr.sin_port=htons(_PORT);inet_pton(AF_INET,_IP,&ServerAddr.sin_addr.s_addr);int Myfd=socket(AF_INET,SOCK_STREAM,0);//看需求决定是否要绑定char Response[1024];//存放服务端反馈信息ssize_t recvlen;bzero(Response,sizeof(Response));if((connect(Myfd,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr)))==0){if((recvlen=recv(Myfd,Response,sizeof(Response),0))>0){printf("%s\n",Response);}}else{printf("Connect failed\n");close(Myfd);exit(0);}close(Myfd);printf("Client is Over\n");return 0;
}

运行结果:

函数二次包裹

在我们编写代码时,通常需要根据函数的返回值来判断函数调用情况,这个过程需要大量的if else语句,减少了代码阅读的简洁性。所以为了方便我们阅读,需要对函数进行二次包裹。

什么是二次包裹?

在自定义的头文件中重新声明一个函数,函数名与接口函数不同,返回值、参数均与接口函数相同。在相应的源文件中定义我们的函数,将逻辑判断的过程放在函数中。这样在调用函数时就省去大量的if else判断

#ifndef __MySock_H__
#define __MySock_H__
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int SOCKET(int domain,int type,int protocol);
int BIND(int sockfd,struct sockaddr* addr,socklen_t addrlen);
ssize_t RECV(int sockfd,void *buf,size_t len,int flags);
ssize_t SEND(int sockfd,void *buf,size_t len,int flags);
int CONNECT(int sockfd,struct sockaddr *addr,socklen_t addrlen);
int ACCEPT(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
int LISTEN(int sockfd,int backlog);#endif
#include "MySock.h"
int SOCKET(int domain,int type,int protocol)
{int reval=socket(domain,type,protocol);    if(reval==-1){perror("socket call failed");   exit(0);}return reval;
}
int BIND(int sockfd,struct sockaddr* addr,socklen_t addrlen)
{int reval=bind(sockfd,addr,addrlen);if(reval==-1){perror("bind call failed");exit(0);}return reval;
}
ssize_t RECV(int sockfd,void *buf,size_t len,int flags)
{ssize_t reval;reval=recv(sockfd,buf,len,flags);if(reval==0)perror("recv call failed");return reval;
}
ssize_t SEND(int sockfd,void *buf,size_t len,int flags)
{ssize_t reval;reval=send(sockfd,buf,len,flags);if(reval==-1)perror("send call failed");return reval;
}
int CONNECT(int sockfd,struct sockaddr *addr,socklen_t addrlen)
{int reval=connect(sockfd,addr,addrlen);if(reval==-1){perror("connect call failed");exit(0);}return reval;
}int ACCEPT(int sockfd,struct sockaddr* addr,socklen_t *addrlen)
{int reval=accept(sockfd,addr,addrlen);if(reval==-1){perror("accept call failed");exit(0);}return reval;
}
int LISTEN(int sockfd,int backlog)
{int reval=listen(sockfd,backlog);if(reval==-1){perror("listen call failed");exit(0);}return reval;
}

使用包裹后的函数对我们TCP通信的客户端进行改写:

代码看起来简洁多了

#include "MySock.h"//客户端源码编写,连接服务器成功,服务器反馈信息#define _IP "xxx.xxx.xxx.xxx"
#define _PORT 8080
int main()
{struct sockaddr_in ServerAddr;bzero(&ServerAddr,sizeof(ServerAddr));ServerAddr.sin_family=AF_INET;ServerAddr.sin_port=htons(_PORT);inet_pton(AF_INET,_IP,&ServerAddr.sin_addr.s_addr);int Myfd=SOCKET(AF_INET,SOCK_STREAM,0);//看需求决定是否要绑定char Response[1024];//存放服务端反馈信息ssize_t recvlen;bzero(Response,sizeof(Response));if((CONNECT(Myfd,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr)))==0){if((recvlen=recv(Myfd,Response,sizeof(Response),0))>0){printf("%s\n",Response);}}close(Myfd);printf("Client is Over\n");return 0;
}

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

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

相关文章

Java基础(四)——字符串、StringBuffer、StringBuilder、StringJoiner

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

【技巧】如何检查多个GPU之间是否支持P2P通信

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 需要用到cuda_samples&#xff1a;GitHub - NVIDIA/cuda-samples 该工具的详细解释可以看这个&#xff1a; 【知识】详细介绍 CUDA Samples 示例工程…

不止是只有维度建模,数据仓库还有Data Vault建模

引言 在数据仓库设计中&#xff0c;传统的星型和雪花型模型有着各自的优势和劣势。随着数据量的增大和数据源的多样化&#xff0c;Data Vault&#xff08;数据仓库&#xff09;建模方法逐渐受到关注和应用。Data Vault建模是一种灵活、可扩展、适应性强的建模方法&#xff0c;…

Java知识点整理 12 — 前端 Ant Design Pro 初始化模板使用

一. 项目初始化 Ant Design Pro 是基于 Ant Design 和 umi 封装的一整套企业级中后台前端设计框架&#xff0c;致力于在设计规范和基本组件的基础上&#xff0c;继续向上构建&#xff0c;提炼出典型模板或配套设计资源&#xff0c;进一步提升企业级中后台产品设计研发过程中的…

用MySQL和navicatpremium做一个项目—(财务管理系统)。

1 ER图缩小的话怕你们看不清&#xff0c;所以截了两张图 2 vsdx绘图结果 3DDL和DML,都有点长分了好多次上传&#xff0c;慢慢看 DDL -- 用户表 CREATE TABLE users (user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID,username VARCHAR(50) NOT NULL UNIQUE COMMENT 用…

SpringCloud分布式微服务链路追踪方案:Skywalking

一、引言 随着微服务架构的广泛应用&#xff0c;系统的复杂性也随之增加。在这种复杂的系统中&#xff0c;应用通常由多个相互独立的服务组成&#xff0c;每个服务可能分布在不同的主机上。微服务架构虽然提高了系统的灵活性和可扩展性&#xff0c;但也带来了新的挑战&#xf…

vue的学习--day2

如有错误&#xff0c;烦请指正~ 目录 一、什么是单页面应用程序 二、使用工具&#xff1a;node.js 三、工具链 易错点 一、什么是单页面应用程序 多个组件&#xff08;例如登录、注册等以vue结尾的都叫做组件&#xff09;在一个页面显示&#xff0c;叫单页面应用…

【软件测试】白盒测试与接口测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是白盒测试 白盒测试是一种测试策略&#xff0c;这种策略允许我们检查程序的内部结构&a…

Vite: Esbuild的使用与其插件开发

概述 作为 Vite 的双引擎之一&#xff0c;Esbuild 在很多关键的构建阶段(如 依赖预编译 、 TS 语法转译 、 代码压缩 ) 让 Vite 获得了相当优异的性能&#xff0c;是 Vite 高性能的得力助手无论是在 Vite 的配置项还是源码实现中&#xff0c;都包含了不少 Esbuild 本身的基本概…

你知道大数据信用分低需要如何改善吗?

在当今社会&#xff0c;大数据信用分已经成为个人信用评估的重要指标之一。然而&#xff0c;有时候我们会发现自己的大数据信用分较低&#xff0c;这可能会对我们的信用状况产生负面影响。那么&#xff0c;如何改善自己的大数据信用分呢?本文将从信用分低的原因进行分析&#…

【Micro-ROS学习】

Micro-ROS 是专为 ROS 2 设计的&#xff0c;它允许在微控制器&#xff08;microcontrollers&#xff09;上实现ROS 2的功能。Micro-ROS 从 ROS 2 架构优化而来&#xff0c;目的是让那些资源有限的嵌入式设备也能够接入ROS 2生态系统&#xff0c;享受ROS 2带来的标准化通信、模块…

java简易计算器(多种方法)

parseDouble() 方法属于 java.lang.Double 类。它接收一个字符串参数&#xff0c;其中包含要转换的数字表示。如果字符串表示一个有效的 double&#xff0c;它将返回一个 double 值。 应用场景 parseDouble() 方法在以下场景中非常有用&#xff1a; 从用户输入中获取数字&a…

国内AI行业对GPU算力的需求有多大?

随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;算力作为支撑其持续进步的核心动力&#xff0c;在国内的重要性日益凸显&#xff0c;无论是海外还是国内&#xff0c;AI算力行业都呈现出蓬勃发展的态势&#xff0c;而国内对于AI算力的需求更是呈现出爆发式的…

离散傅里叶变化

傅里叶变换 对傅里叶变换了解不是很清楚的朋友推荐一下这个帖子&#xff0c;讲得很详细 傅里叶变换 源码 先看源码链接 #include "opencv2/core.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "open…

从零开始了解GPT-4o模型:它是如何工作的?

人工智能&#xff08;AI&#xff09;技术正以惊人的速度发展&#xff0c;其中最引人注目的是OpenAI发布的GPT-4o模型。作为GPT系列的新成员&#xff0c;GPT-4o在多模态输入处理和响应速度上取得了重大进展。本文将深入探讨GPT-4o的工作原理&#xff0c;帮助您全面了解这一尖端A…

MySQL实训

项目名称与项目简介 股票交易系统是一个综合性的金融服务平台&#xff0c;它提供了股票买卖、交易查询、用户管理、股票信息管理以及资金账户管理等功能。系统旨在为用户提供一个安全、高效、便捷的股票交易环境&#xff0c;让用户能够实时掌握市场动态&#xff0c;做出合理的…

模拟物理弧线轨道运动(模拟飞盘,子弹运动)

模拟物理弧线运动&#xff08;模拟飞盘&#xff09; 介绍实现代码总结 介绍 模拟弧线的运动&#xff0c;并且对象始终朝向运动的方向&#xff0c;模拟飞盘子弹的运动轨迹。这里我是没有加重力这么一个概念的&#xff0c;当然了重力其实比较简单可以参考我之前写的模拟抛物线运动…

ubuntu 挂载新硬盘 记录

Ref 安全自动挂载硬盘&#xff0c; https://berylbot.com/archives/mount-disks-ubuntu 挂载新硬盘, https://berylbot.com/archives/mount-disks-ubuntu 1. 检查新硬盘是否被系统识别 lsblk -f 查看所有硬盘的UUID, 其中 mount point 为空则表示尚未挂载的硬盘。 列出所有可用…

ET9中ETTask传递新的Context原理

ET9中ETTask传递新的Context原理 前言 每一个异步函数都会创建两个对象&#xff0c; 第1个是当前异步函数返回值&#xff08;ETTASK&#xff09;对应的ETAsyncTaskMethodBuilder&#xff0c;通过这个类的静态方法Create创建返回&#xff0c;这个builder类中会有一个Task对象&…

idea 自动生成序列化数字

目标&#xff1a;当类继承Serializable后自动生成序列化Uid 网上查了很多说勾选class without ‘serialVersionUID’ 但是我勾选没用 最后发现&#xff0c;我勾选的是Serialization issues里面的配置&#xff0c;要勾选的是JVM languages下的 如下图所示&#xff0c;记录一下…