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

news2024/12/26 2:53:23

🔥博客主页: 我要成为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 1500


int 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.coloradmin.cn/o/1870347.html

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

相关文章

揭秘数据合并的秘密:一文掌握一对一、多对一、多对多合并技巧与实战!

使用pd.merge()合并 类似 MySQL 中表和表直接的合并merge与concat的区别在于,merge需要依据某一共同的行或列来进行合并使用pd.merge()合并时,会自动根据两者相同column名称的那一列,作为key来进行合并每一列元素的顺序不要求一致1. 一对一合并 df1 = pd.DataFrame({"…

搜维尔科技:SenseGlove Nova2国内首款支持手掌心力回馈手套开售

《SenseGlove Nova 2》现正全球发行中! 搜维尔科技独家代理最新上市的 SenseGlove Nova 2 是世上首款&#xff0c;也是目前市面上唯一一款提供手掌力回馈的无缐VR力回馈手套&#xff0c;它结合了三种最先进的反馈技术&#xff0c;包括主动反馈、强力反馈及震动反馈&#xff0c…

k8s学习笔记(一)

configMap 一般用来存储配置信息 创建configMap 从文件中获取信息创建&#xff1a;kubectl create configmap my-config --from-file/tmp/k8s/user.txt 直接指定信息&#xff1a; kubectl create configmap my-config01 --from-literalkey1config1 --from-literalkey2confi…

小九首度回应与小水分手传闻揭秘

#小九首度回应&#xff01;与小水分手传闻揭秘#近日&#xff0c;泰国娱乐圈掀起了一股热议的狂潮&#xff01;传闻中的“金童玉女”组合——“小水”平采娜与“小九”NINE疑似分手的消息&#xff0c;如同巨石投入平静的湖面&#xff0c;激起了千层浪花。而在这股狂潮中&#xf…

CesiumJS【Basic】- #020 加载glb/gltf文件(Primitive方式)

文章目录 加载glb/gltf文件(Primitive方式)1 目标2 代码实现3 资源文件加载glb/gltf文件(Primitive方式) 1 目标 使用Primitive方式加载glb/gltf文件 2 代码实现 import * as Cesium from "cesium";const viewer = new Cesium.Viewer

x264 码率控制 VBV 算法原理:数学模型与数据流转

x264 码率控制 VBV 算法原理 关于 VBV原理的分析可以参考:x264 码率控制 VBV 原理。关于 VBV 算法的源码分析可以参考:x264 码率控制中实现 VBV 算法源码分析。VBV算法介绍 x264中的VBV(Video Buffering Verifier)算法是H.264编码标准的一部分,主要用于码率控制,确保视频…

C语言实战 | “俄罗斯方块”游戏重构

之前的游戏中,为了方便大家掌握框架,在“贪吃蛇”游戏中使用了大量的全局变量。全局变量空间利用率不高,全局变量在程序的执行过程中一直占用存储单元,而不是仅在需要时才开辟单元。另外,全局变量降低了通用性,程序执行时还需要依赖全局变量。例如,显示“食物”和“球”…

计算机的错误计算(十四)

摘要 解读 GPU和CPU计算上的精度差异&#xff1a;GPU 的 3个输出的相对误差分别高达 62.5%、50%、62.5%。 例1. 计算下列两个矩阵的乘积&#xff1a; 显然&#xff0c;其结果为第一列&#xff1a; 这个结果是准确的。 例2. 已知上面 3 个矩阵是由下面代码产生或输出&…

Zynq7000系列FPGA中的定时器详细介绍

每个Cortex-A9处理器都有自己的专用32位定时器和32位看门狗定时器。两个处理器共享一个全局64位定时器。这些定时器总是以CPU频率&#xff08;CPU_3x2x&#xff09;的1/2进行计时。 在系统级&#xff0c;有一个24位看门狗定时器和两个16位三重定时器/计数器。 系统看门狗定时器…

免费分享一套SpringBoot+Vue在线水果(销售)商城管理系统【论文+源码+SQL脚本】,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue在线水果(销售)商城管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue在线水果(销售)商城管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue在线水果(销售)商…

【海思Hi3403V100】多目拼接相机套板硬件规划方案

海思Hi3403V100 是专业超高清智能网络摄像头 SoC。该芯片最高支持四路 sensor 输入&#xff0c;支持最高 4K60fps 的 ISP 图像处理能力&#xff0c;支持 3F 、WDR、多级降噪、六轴防抖、硬件拼接、多光谱融合等多种传统图像增强和处理算法&#xff0c;支持通过AI 算法对输入图像…

CAN-bus总线在冷链运输中的应用

CAN-bus总线在冷链运输中的应用 如图1所示,疫苗冷链是指为保证疫苗从疫苗生产企业到接种单位运转过程中的质量而装备的存储、运输冷藏设施、设备。由于疫苗对温度敏感,从疫苗制造的部门到疫苗使用的现场之间的每一个环节,都可能因温度过高而失效。在储运过程中,一旦温度超…

全面解读OA系统:功能、价值及应用

反复沟通、来回跑腿&#xff0c;还易出错&#xff1b; 纸笔记录、excel统计&#xff0c;效率低耽误事&#xff1b; 档案、物资&#xff0c;查不清记录、看不了实时&#xff1b; 部门各做各的、各管各的&#xff0c;沟通配合难…… 你有没有经历过诸如上述的繁琐办公流程&am…

Transformer教程之神经网络和深度学习基础

在当今的人工智能领域&#xff0c;Transformer已经成为了一个热门的词汇。它不仅在自然语言处理&#xff08;NLP&#xff09;领域取得了巨大的成功&#xff0c;还在计算机视觉等其他领域展现出了强大的潜力。然而&#xff0c;要真正理解Transformer&#xff0c;我们首先需要扎实…

用FFmpeg合并音频和视频

使用FFmpeg合并音频和视频是一个相对直接的过程。可以通过以下一些基本的步骤和命令示例完成这个任务&#xff1a; 安装FFmpeg&#xff1a;首先&#xff0c;确保你的系统中已经安装了FFmpeg。你可以从[FFmpeg官网](Download FFmpeg)下载并安装它。 准备素材&#xff1a;确保你…

高考未上本科线,大专不是唯一归宿

高考&#xff0c;作为人生中的一次重要考试&#xff0c;其结果往往牵动着无数家庭的心。然而&#xff0c;当高考成绩未能达到本科线时&#xff0c;是否就意味着大专是唯一的选择呢&#xff1f;其实不然&#xff0c;现代教育体系的多样化为我们提供了更多的可能性&#xff0c;其…

ElementPlus Combogrid 组件

效果图: 1.声明 Props类型 export type comboGridPropType { modelValue: any; url: string; keyField?: string; labelField?: string; filterOptions?: Array<ISearchOption>; tableColumns?: Array<TableColumns>; enableField?: string; multiple?: …

【机器学习】深度概率模型(DPM)原理和文本分类实践

1.引言 1.1.DPM模型简介 深度概率模型&#xff08;Deep Probabilistic Models&#xff09; 是结合了深度学习和概率论的一类模型。这类模型通过使用深度学习架构&#xff08;如神经网络&#xff09;来构建复杂的概率分布&#xff0c;从而能够处理不确定性并进行预测。深度概率…

1.1章节print输出函数语法八种 使用和示例

1.打印变量和字符串 2-4.三种使用字符串格式化 5.输出ASCLL码的值和中文字符 6.打印到文件或其他对象&#xff08;而不是控制台&#xff09; 7.自定义分隔符、和换行符和结束符 8.连接符加号连接字符串 在Python中&#xff0c;print() 函数用于在控制台上输出信息。这是一个非常…

【Proteus仿真】基于stm32的数码管时钟

【Proteus仿真】基于stm32的数码管时钟 Proteus仿真&#xff01;基于stm32的数码管时钟~_哔哩哔哩_bilibili ‍ 01原理图 ​​ 02功能描述 1.通过按键修改时间 2.数码管显示实时时间&#xff0c;时-分-秒-毫秒格式 03获取方式 https://docs.qq.com/sheet/DTExIc2dPUUJ…