Linux网络编程1-简单的CS通信程序

news2024/11/21 0:39:18

Linux网络编程1-简单的CS通信程序

    • 1.Socket相关API说明
      • 1.1字节序转换函数:用于ip和port转换
      • 1.2sockaddr结构
      • 1.3socket函数 以及两个队列
      • 1.4bind listen connect accept
      • 1.5收发数据
    • 2.服务器和客户端程序代码流程
    • 3.服务器端
    • 4.客户端
    • 5.测试accept不是建立连接而是从已连接的队列中取出一个通信


1.Socket相关API说明

1.1字节序转换函数:用于ip和port转换

网络字节序是大端字节序:低位地址存放高位数据, 高位地址存放低位数据。一般主机字节序是小端字节序:低位地址存放低位数据, 高位地址存放高位数据。

0x12345678
大端存储:12 34 56 78
小端存储: 78 56 34 12

小端字节序需要转换为大端字节序:

整形的转换函数:一般用于端口htons。

#include <arpa/inet.h>
// 主机到网络
uint32_t htonl(uint32_t hostlong); 
uint16_t htons(uint16_t hostshort);
// 网络到主机
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

IP地址转换函数:

int inet_pton(int af, const char *src, void *dst); // 点分十进制转换为网络字节序 
// inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr); 127.0.0.1 -> [100007f]
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); // 网络字节序转换为点分十进制IP地址

1.2sockaddr结构

struct sockaddr {
    typedef unsigned short int sa_family_t;
    sa_family_t sa_family; // AF_INET
    char     sa_data[14];  // 地址信息:2个字节存放port,4个字节存放ip,剩下8个字节空闲
}

使用sockaddr结构不方便,一般使用sockaddr_in结构,两个结构体大小相同。

struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr {
    in_addr_t s_addr;
};

如果函数需要sockaddr结构,可以使用sockaddr_in变量,然后强转成sockaddr结构:

struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_port = htons(6789);
// 服务器的ip地址:点分十进制ip地址转换为网络ip
inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);

int ret = connect(cfd, (struct sockaddr *)&serv, sizeof(serv));

1.3socket函数 以及两个队列

int socket(int domain, int type, int protocol);

/*
domain: 协议版本
AF_INET IPV4
AF_INET6 IPV6
AF_UNIX AF_LOCAL本地套接字使用

type:协议类型
SOCK_STREAM 流式, 默认使用的协议是TCP协议
SOCK_DGRAM  报式, 默认使用的是UDP协议

protocal: 
一般填0, 表示使用对应类型的默认协议.

成功: 返回一个大于0的文件描述符
失败: 返回-1, 并设置errno
*/
int cfd = socket(AF_INET, SOCK_STREAM, 0);

当调用socket函数以后, 返回一个文件描述符, 内核会提供与该文件描述符相对应的读和写缓冲区, 同时还有两个队列, 分别是请求连接队列已连接队列.

在这里插入图片描述

1.4bind listen connect accept

bind:将socket文件描述符和IP,PORT绑定

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
成功: 返回0
失败: 返回-1, 并设置errno
*/
struct sockaddr_in serv;
bzero(&serv, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(6789); // 主机到网络字节序
serv.sin_addr.s_addr = htonl(INADDR_ANY); // 使用本机任意有效的可用IP
int ret = bind(lfd, (struct sockaddr *) &serv, sizeof(serv));
if (ret < 0)
{
    perror("bind error");
    return -1;
}

listen:将套接字由主动态变为被动态:

int listen(int sockfd, int backlog);
/*
backlog: 同时请求连接的最大个数(还未建立连接) 

成功: 返回0
失败: 返回-1, 并设置errno
*/
ret = listen(lfd, 128); // 请求连接的最大个数128
if(ret < 0)
{
    perror("listen error");
    return -1;
}

accept:获得一个连接,,若当前没有连接则会阻塞等待.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
addr: 传出参数, 保存客户端的地址信息
addrlen: 传入传出参数,  addr变量所占内存空间大小

成功: 返回一个新的文件描述符,用于和客户端通信
失败: 返回-1, 并设置errno值.
*/
struct sockaddr_in client;
socklen_t len = sizeof(client);
int cfd = accept(lfd, (struct sockaddr *)&client, &len);
if(cfd < 0)
{
    perror("accpet error");
    return -1;
}

accept函数是一个阻塞函数, 若没有新的连接请求, 则一直阻塞。从已连接队列中获取一个新的连接, 并获得一个新的文件描述符, 该文件描述符用于和客户端通信. (内核会负责将请求队列中的连接拿到已连接队列中)

调用accept函数不是说新建一个连接,而是从已连接队列中取出一个可用连接.

connect:连接服务器

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
addr: 服务端的地址信息

成功: 返回0
失败: 返回-1, 并设置errno值
*/
int ret = connect(cfd, (struct sockaddr *)&serv, sizeof(serv));
if(ret<0)
{
    perror("connect error");
    return -1;
}

1.5收发数据

可以使用write和read函数进行读写操作。还可以使用recv和send函数。

// 读取数据和发送数据:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

// 对应recv和send这两个函数flags直接填0就可以了.
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);	

注意: 如果写缓冲区已满, write也会阻塞, read读操作的时候, 若读缓冲区没有数据会引起阻塞.

2.服务器和客户端程序代码流程

在这里插入图片描述

3.服务器端

#include <iostream>

#include <sys/types.h>
#include <sys/socket.h> // socket
#include <netinet/in.h> // sockaddr_in
#include <cstring> // bzero
#include <unistd.h> // read

int main()
{
    std::cout << "STR_TEST: " << STR_TEST << std::endl;
    // 1.创建监听套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd < 0) {
        perror("socket error");
        return -1;
    }

    // 2.绑定本地ip和port
    struct sockaddr_in serv;
    bzero(&serv, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(6789); // 主机到网络字节序
    serv.sin_addr.s_addr = htonl(INADDR_ANY); // 使用本机任意有效的可用IP
    int ret = bind(lfd, (struct sockaddr *) &serv, sizeof(serv));
    if (ret < 0)
    {
        perror("bind error");
        return -1;
    }

    // 3.监听端口
    ret = listen(lfd, 128); // 请求连接的最大个数128
    if(ret < 0)
    {
        perror("listen error");
        return -1;
    }

    // 4.从连接队列中取出一个通信
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    int cfd = accept(lfd, (struct sockaddr *)&client, &len);
    if(cfd < 0)
    {
        perror("accpet error");
        return -1;
    }

    // 5.读写数据
    while(1)
    {
        char buf[1024];
        bzero(buf, sizeof(buf));

        ssize_t n = read(cfd, buf, sizeof(buf));
        if(n <= 0)
        {
            std::cout << "read error or client close, n=" << n << std::endl;
            break;
        }
        std::cout << "n=" << n << ", buf=" << buf << std::endl;

        // 将接收的数据转为大写,然后发送给客户端
        for(int i= 0;i<n;++i)
        {
            buf[i] = toupper(buf[i]);
        }
        n = write(cfd, buf, n);
        if(n < 0)
        {
            perror("write error");
            break;
        }
    }

    close(cfd);
    close(lfd);

    return 0;
}

4.客户端

#include <iostream>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> // inet_pton
#include <cstring>
#include <unistd.h> // read write

int main(int argc, char* argv[])
{
    std::cout << "STR_TEST: " << STR_TEST << std::endl;
    // 1.创建socket
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd<0)
    {
        perror("socket error");
        return -1;
    }
    struct in_addr
    // 2.连接服务端
    struct sockaddr_in serv;
    serv.sin_family = AF_INET;
    serv.sin_port = htons(6789);
    // 服务器的ip地址:点分十进制ip地址转换为网络ip
    inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
    //printf("[%x]\n", serv.sin_addr.s_addr); [100007f]
    int ret = connect(cfd, (struct sockaddr *)&serv, sizeof(serv));
    if(ret<0)
    {
        perror("connect error");
        return -1;
    }

    while(1)
    {
        char buf[256];
        //读标准输入数据
        bzero(buf, sizeof(buf));
        int n = read(STDIN_FILENO, buf, sizeof(buf));

        //发送数据
        write(cfd, buf, n);

        //读服务端发来的数据
        bzero(buf, sizeof(buf));
        n = read(cfd, buf, sizeof(buf));
        if(n<=0)
        {
            std::cout << "read error or server closed, n=" << n << std::endl;
            break;
        }
        std::cout << "n=" << n << ", buf=" << buf << std::endl;

    }

    //关闭套接字cfd
    close(cfd);

    return 0;
}

5.测试accept不是建立连接而是从已连接的队列中取出一个通信

测试过程中可以使用netstat命令查看监听状态和连接状态

netstat命令:

a表示显示所有,

n表示显示的时候以数字的方式来显示

p表示显示进程信息(进程名和进程PID)

accept函数是从已连接队列中取出一个通信,可以在调用accept函数之前sleep一分钟,在此期间,执行客户端连接到服务器,查看端口监听状态,sleep结束后会accept,然后再查看端口监听状态。

1.执行Server accpet前等待30s
2.执行Client
3.netstat -anp | grep 6789 查看端口状态 连接已建立。
4.30s过后
5.再次执行netstat -anp | grep 6789 查看端口状态

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

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

相关文章

1803_ChibiOS网络书籍阅读_嵌入式RTOS介绍

全部学习汇总&#xff1a; GreyZhang/g_ChibiOS: I found a new RTOS called ChibiOS and it seems interesting! (github.com) 1. RTOS指的是实时性操作系统&#xff0c;但是并不是只有嵌入式领域使用RTOS。然而&#xff0c;嵌入式是RTOS的主要使用领域。 2. 一般的RTOS有一组…

国庆中秋宅家自省:偷偷尝鲜

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 Python3数据科学包系列(三):数据分析实战 国庆中秋宅家自省: Pytho…

【visual studio 小技巧】项目属性->生成->事件

需求 我们有时会用到一些dll&#xff0c;需要把这些dll和我们生成的exe放到一起&#xff0c;一般我们是手动自己copy&#xff0c; 这样发布的时候&#xff0c;有时会忘记拷贝这个dll&#xff0c;导致程序运行出错。学会这个小技巧&#xff0c;就能实现自动copy&#xff0c;非…

Ubuntu1804 安装后无法使用root登录解决方法

1. 给root用户设置密码 sudo passwd root2. 确认是否安装ssh服务 (在安装Ubuntu 的时候可以勾选安装ssh 远程服务),没有安装的话执行以下命令(Ubuntu可以连接互联网) sudo apt-get instll openssh-server3. 设置允许root 用户进行远程连接 sudo vim /etc/ssh/sshd_config 在…

10.3 调试事件转存进程内存

我们继续延申调试事件的话题&#xff0c;实现进程转存功能&#xff0c;进程转储功能是指通过调试API使获得了目标进程控制权的进程&#xff0c;将目标进程的内存中的数据完整地转存到本地磁盘上&#xff0c;对于加壳软件&#xff0c;通常会通过加密、压缩等手段来保护其代码和数…

【Ubuntu】基于C++实现人脸识别

人脸识别考勤机 文章目录 人脸识别考勤机概述第一章 搭建Ubuntu环境1.1 什么是物联网1.2 物联网应该怎么学1.3 Linux开发环境搭建1.4 Linux基本使用1.5 Ubuntu网络配置 第二章 “hello,world!”程序2.1 什么是程序2.2 “hello,world!”程序2.3 C语法扩展2.4 常见错误调试 第三章…

Numpy科学计算基础库--numpy基础知识

对数组执行数学运算和逻辑运算时&#xff0c;Numpy 是非常有用的。在用 Python 对 n 维数组和矩阵进行运算时&#xff0c;Numpy 库提供了大量有用特征。Numpy 库数组有两种形式&#xff1a;向量和矩阵。严格地讲&#xff0c;向量是一维数组&#xff0c;矩阵是多维数组。在某些情…

Redis-数据过期策略

数据过期策略 惰性删除策略优点&#xff1a;对cpu比较友好&#xff0c;在用到该key的时候才去进行判断&#xff0c;对于很多用不到key不用浪费时间去检查是否过期缺点&#xff1a;对内存不友好&#xff0c;如果一个key过期了&#xff0c;但是我们又一直没有用到该key&#xff0…

基于YOLOv8的安全帽检测系统(4):EMA基于跨空间学习的高效多尺度注意力、效果优于ECA、CBAM、CA,助力行为检测 | ICASSP2023

目录 1.Yolov8介绍 2.安全帽数据集介绍 3.EMA介绍 4.训练结果分析 5.系列篇 1.Yolov8介绍 Ultralytics YOLOv8是Ultralytics公司开发的YOLO目标检测和图像分割模型的最新版本。YOLOv8是一种尖端的、最先进的&#xff08;SOTA&#xff09;模型&#xff0c;它建立在先前YOLO…

Win11 安装 Vim

安装包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Ru7HhTSotz9mteHug-Yhpw?pwd6666 提取码&#xff1a;6666 双击安装包&#xff0c;一直下一步。 配置环境变量&#xff1a; 先配置系统变量中的path&#xff1a; 接着配置用户变量&#xff1a; 在 cmd 中输入…

MySQL 事务隔离级别与锁机制详解

目录 一、前言二、事务及其ACID属性三、并发事务处理带来的问题四、事务隔离级别4.1、隔离级别分类4.2、查看当前数据库的事务隔离级别:4.3、临时修改数据库隔离级别&#xff08;重启MySQL后恢复到配置中的级别&#xff09; 五、表数据准备六、MySQL常见锁介绍5.1、锁分类5.2、…

【网络安全 --- XSS漏洞利用实战】你知道如何利用XSS漏洞进行cookie获取,钓鱼以及键盘监听吗?--- XSS实战篇

一&#xff0c;XSS 实战 以pikachu靶场为例 1-1 盗取cookie 过程&#xff1a;想要盗取别人的cookie信息的话有一个前提条件&#xff0c;就是你应该在别人触发你的xss攻击时&#xff0c;你的代码应该将收集的cookie信息发送给你的平台来接收&#xff0c;这样才获取到了数据 …

图的广度遍历-邻接矩阵实现

description 本题要求实现邻接矩阵存储图的广度优先遍历。 函数接口定义&#xff1a; void BFS(MGraph G,Vertex i); 其中MGraph是邻接矩阵存储的图&#xff0c;定义如下&#xff1a; #define MaxVertexNum 10 /定义最大顶点数/ typedef int Vertex;/* 用顶点下标表示顶点,…

1799_GNU pdf阅读器evince_windows系统下编译尝试

全部学习汇总&#xff1a; GreyZhang/g_GNU: After some years I found that I do need some free air, so dive into GNU again! (github.com) 从网上下载下来了evince的代码&#xff0c;尝试做一个windows下的编译。 这应该是autotools的构建系统&#xff0c;先尝试运行confi…

java做个qq机器人

前置的条件 机器人是基于mirai框架实现的。根据官方的文档&#xff0c;建议使用openjdk11。 我这里使用的编辑工具是idea2023 在idea中新建一个maven项目&#xff0c;虽然可以使用gradle进行构建&#xff0c;不过我这里由于网络问题没有跑通。 pom.xml <dependency>&l…

提示msvcp140.dll丢失的5个解决方法,msvcp140.dll丢失问题全面分析

在我们的日常生活和工作中&#xff0c;电脑已经成为不可或缺的工具。然而&#xff0c;在使用电脑的过程中&#xff0c;我们经常会遇到各种问题&#xff0c;其中就包括提示 msvcp140.dll 丢失的问题。msvcp140.dll 是 Visual C Redistributable for Visual Studio 2015 的运行时…

堆--数组中第K大元素

如果对于堆不是太认识&#xff0c;请点击&#xff1a;堆的初步认识-CSDN博客 解题思路&#xff1a; /*** <h3>求数组中第 K 大的元素</h3>* <p>* 解体思路* <ol>* 1.向小顶堆放入前k个元素* 2.剩余元素* 若 < 堆顶元素, 则略过* …

SM5308 2.1A 充电 2.4 A 放电高集成度移动电源 SOC 可替代IP5306

SM5308 是一款集成升压转换器、锂电池充电管理、电池电量指示的多功能电源管理 SOC&#xff0c;为移动电源 提供完整的电源解决方案。 SM5308 的高集成度与丰富功能,使其在应用时仅需极少的外围器件,并有效减小整体方案的尺寸&#xff0c; 降低 BOM 成本。 SM5308 只需一个电…

竞赛选题 深度学习 opencv python 实现中国交通标志识别

文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 &#x1f525; 优质…

1797_GNU pdf阅读器evince

全部学习汇总&#xff1a; GreyZhang/g_GNU: After some years I found that I do need some free air, so dive into GNU again! (github.com) 近段时间经历了很多事情&#xff0c;终于想找一点技术上的自由气氛。或许&#xff0c;没有什么比GNU的一些软件探索更适合填充这样的…