C/S客户端核服务端-简单收发

news2024/11/24 12:50:27

一、程序

首先上程序

client端的程序

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32

int test_client()
{
    printf("%s %d \n", __func__, __LINE__);

    int fd = -1;
    struct sockaddr_in addr_in;
    char buf[SERVER_SIZE];
    /*
    AF_INET:IPV4
    SOCK_STREAM:TCP
    */
    // 1、创建socket 得到fd
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        printf("%s %d fd<0\n", __func__, __LINE__);
        return 0;
    }

    /*
    2、连接
    */
    memset(&addr_in, 0, sizeof(addr_in));  // 将变量addr_in置0
    addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
    addr_in.sin_family = AF_INET;
    addr_in.sin_addr.s_addr = inet_addr(SERVER_HOST); // 将主机从点分形式转换为32字节

    int reccon = connect(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
    if (reccon < 0)
    {
        printf("%s %d reccon<0\n", __func__, __LINE__);
        return 0;
    }

    // 读写

    while (1)
    {
        memset(buf, 0, SERVER_SIZE);
        char *rec_p = fgets(buf, SERVER_SIZE - 1, stdin);

        int recwrite = write(fd, buf, strlen(buf));
        if (recwrite > 0)
        {
            printf("%s %d buf = %s\n", __func__, __LINE__, buf);
            // int recstr = strcmp(buf, SERVER_QUIT);
            int recstr = strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT));
            printf("%s %d recstr = %d\n", __func__, __LINE__, recstr);
            if (0 == recstr)
            {
                printf("%s %d q======\n", __func__, __LINE__);
                break;
            }
        }

        usleep(100);
    }
    close(fd);
    printf("%s %d xxxxxxxxxxxxxx\n", __func__, __LINE__);
    return 0;
}

server端的程序

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32


int test_server()
{
    printf("%s %d \n", __func__, __LINE__);

    int fd = -1;
    struct sockaddr_in addr_in;
    char buf[SERVER_SIZE];
    /*
    AF_INET:IPV4
    SOCK_STREAM:TCP
    */
    // 1、创建socket 得到fd
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        printf("%s %d fd<0\n", __func__, __LINE__);
        return 0;
    }

    // 2、绑定
    memset(&addr_in, 0, sizeof(addr_in));  // 将变量addr_in置0
    addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
    addr_in.sin_family = AF_INET;
    // int rec = inet_pton(AF_INET,SERVER_HOST,(void*)&addr_in.sin_addr.s_addr);
    //    if(rec!= 1){
    //     printf("%s %d rec!= 1\n", __func__, __LINE__);
    //         return 0;
    //    }

    addr_in.sin_addr.s_addr = inet_addr(SERVER_HOST); // 将主机从点分形式转换为32字节
    int rec = bind(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
    if (rec < 0)
    {
        printf("%s %d rec<0\n", __func__, __LINE__);
        return 0;
    }

    // 3、listen() 将主动套接字转换为被动套接字
    int reclisten = listen(fd, SERVER_BACKLOG); // 允许正在进行连接的客户端数目
    if (reclisten < 0)
    {
        printf("%s %d reclisten<0\n", __func__, __LINE__);
        return 0;
    }
    // 4、阻塞等待客户端连接请求
    int newfd = accept(fd, NULL, NULL);
    if (newfd < 0)
    {
        printf("%s %d newfd<0\n", __func__, __LINE__);
        return 0;
    }

    // 5、读写
    // 和最新的newfd进行通信

    while (1)
    {
        memset(buf, 0, SERVER_SIZE);
        int recread = read(newfd, buf, SERVER_SIZE - 1);
        if (recread > 0)
        {
            printf("%s %d buf = %s\n", __func__, __LINE__, buf);
            if (!strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT)))
            {
                printf("%s %d q======\n", __func__, __LINE__);
                break;
            }
        }

        usleep(100);
    }
    close(fd);
    close(newfd);
    printf("%s %d xxxxxxxxxxxxxx\n", __func__, __LINE__);
    return 0;
}

使用方法

1、新建两个程序,分别引用两个函数,先执行server端的程序,再执行client端的程序
2、实现功能:当client和sever连接成功后,从client输入什么都会传输给server端,当输入第一个字母为q时 两端程序都会退出
3、特别注意:需要修改SERVER_HOST 为自己主机地址
4、本程序编写的环境,如果时windows下执行可能需要修改头文件什么的,耐心一点看就好

gcc -v
gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04.1)

cmake -version
cmake version 3.22.1

make -v
GNU Make 4.3

基本顺序

客户端的函数主要就是socket和connect,服务器端主要就是socket,bing,listen和accept函数,write和read函数和操作文件读写时说一样的使用方法。
在这里插入图片描述

函数细节

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

使用man socket可以查看函数细节
需要包含的头文件:

#include <sys/types.h>
#include <sys/socket.h>

函数原型:

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

domain:指定网络通信使用的协议族或地址族,常见的值包括
AF_INET : IPv4 Internet protocols
AF_INET6 : IPv6 Internet protocols

type:指定套接字的类型,常见的值包括
SOCK_STREAM:提供面向连接的、可靠的、双向字节流的服务,使用TCP协议。
SOCK_DGRAM:提供无连接的、不可靠的、具有固定最大长度的消息传递服务,使用UDP协议。
SOCK_RAW:提供原始套接字访问,可以直接发送和接收底层协议的数据包。

protocol:指定具体的传输协议,一般情况下可以设置为0,由操作系统自动选择合适的协议。

返回值:
创建成功时,返回一个新的套接字描述符(socket descriptor),用于后续的网络通信操作。
创建失败时,返回 -1,并设置全局变量 errno 表示具体的错误类型。

这个地方的返回就是相当于文件描述符,要记得记录,后面需要一直用到

重点:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

sockfd:要绑定的套接字描述符。

addr:指向用于绑定的地址结构体的指针,类型为 struct sockaddr*。

addrlen:地址结构体的长度。

这个函数首先要关注const struct sockaddr *addr,将代码跳转过去可以看到sockaddr 的定义如下所示,但是在实际的应用当中我们却不定义struct sockaddr 类型的变量,而是定义struct sockaddr_in的变量,最后进行强制类型转换填入函数bind。

解释原因:

bind() 函数的 addr 参数要求传入一个指向 struct sockaddr 结构体的指针,以指定要绑定的地址信息。但是,由于struct sockaddr是一个通用的结构体类型,只包含地址族和地址数据,并没有特定协议族的详细信息。因此,在实际应用中,我们使用具体的地址结构体类型来填充 struct sockaddr

struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };

例如,在使用IPv4协议(AF_INET)时,我们使用 struct sockaddr_in 结构体来表示IPv4地址信息,它定义如下:

struct sockaddr_in {
    sa_family_t    sin_family;  // 地址族(协议族)
    in_port_t      sin_port;    // 端口号
    struct in_addr sin_addr;    // IPv4地址
    unsigned char  sin_zero[8]; // 填充字段(通常设置为0)
};

在实际应用中,我们通常会创建一个 struct sockaddr_in 类型的变量,填充需要的地址信息,然后将其转换为 struct sockaddr* 类型的指针传递给 bind() 函数。这样可以确保在绑定时使用了特定协议族的地址结构体,以便正确地指定地址和端口

uint16_t htons(uint16_t hostshort);

htons() 是一个函数,用于将一个16位无符号整数(hostshort)从主机字节序(Host Byte Order)转换为网络字节序(Network Byte Order)

在计算机网络中,字节序指定了多字节数据在内存中的存储顺序。主机字节序是指当前主机所使用的字节序,而网络字节序是一种标准的字节序,用于在不同主机之间进行数据交换。

htons() 函数的参数 hostshort 是一个16位无符号整数(uint16_t 类型),表示需要转换的主机字节序的值。

函数的返回值是一个16位无符号整数,表示转换为网络字节序后的值

该函数名中的 “htons” 表示 “host to network short”,其中 “short” 表示16位整数类型。类似的函数还有 htonl() 用于转换32位整数。

例如,假设我们有一个16位整数 value 需要在网络中传输,我们可以使用 htons() 函数将其转换为网络字节序:

uint16_t value = 5001;  // 假设需要转换的主机字节序的端口号
uint16_t networkValue = htons(value);  // 将主机字节序转换为网络字节序

in_addr_t inet_addr(const char *cp);

inet_addr() 是一个函数,用于将一个点分十进制字符串形式的IPv4地址转换为32位无符号整数(in_addr_t 类型)的网络字节序表示。

函数的参数 cp 是一个指向以空字符结尾的字符串的指针,该字符串表示一个IPv4地址。

函数的返回值是一个32位无符号整数,表示转换后的网络字节序的IPv4地址。如果转换失败,则返回 INADDR_NONE。

inet_addr()
函数将点分十进制表示的IPv4地址转换为网络字节序的32位整数表示。IPv4地址由四个用点分隔的十进制数表示(例如:192.168.0.1)。该函数将这种字符串形式的IPv4地址转换为二进制格式,以便在网络中进行传输和处理。

以下是使用 inet_addr() 函数的示例:

const char *ipAddress = "192.168.0.1";
in_addr_t addr = inet_addr(ipAddress);

int listen(int sockfd, int backlog);

listen() 函数用于将一个套接字(socket)设置为被动模式,并开始监听传入的连接请求。

参数说明:

sockfd:要监听的套接字描述符。
backlog:定义了连接请求队列的最大长度。

在网络编程中,当一个套接字处于监听状态时,它可以接收传入的连接请求。listen()
函数将套接字设置为监听模式,并指定传入连接请求的最大队列长度。

调用 listen() 函数时,套接字必须先通过 bind() 函数绑定到一个具体的地址和端口上。然后,使用 listen()
函数来指定该套接字的监听参数。

在监听状态下,当有新的连接请求到达时,操作系统会将其放入连接请求队列中。队列的长度由 backlog
参数指定,超过队列长度的连接请求将被拒绝

需要注意的是,listen() 函数仅适用于面向连接的套接字(如 TCP 套接字)。对于无连接的套接字(如 UDP 套接字),不需要调用 listen() 函数。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept() 函数用于从已监听的套接字中接受一个传入的连接请求,并创建一个新的套接字来与客户端进行通信。

参数说明:

sockfd:已监听的套接字描述符。

addr:指向用于存储客户端地址信息的结构体的指针,类型为 struct sockaddr*。

addrlen:指向一个整数值,表示 addr 结构体的长度的指针。 flags:可选的标志参数,通常设置为0。

函数在调用时会阻塞,直到有客户端发起连接请求。当一个连接请求到达监听的套接字时,

函数会接受该请求,并创建一个新的套接字来与客户端进行通信。该新创建的套接字用于与客户端之间的数据传输。

函数返回值是一个新创建的套接字描述符,用于与客户端进行通信。
如果出现错误,返回值为 -1,并设置相应的错误码。

在上面的代码当中 第二个和低三个参数都填了NULL,是目前代码当中没有用到连接client的信息,如果需要用到,和前面定义server端 的方式类似

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

connect() 函数用于在套接字上建立与远程服务器的连接。

参数说明:

sockfd:套接字描述符,用于标识要进行连接的套接字。 addr:指向远程服务器地址信息的结构体指针,类型为 struct sockaddr*。

addrlen:远程服务器地址信息结构体的长度。

调用 connect()函数时,客户端套接字尝试与远程服务器建立连接。需要在调用 connect() 函数之前,先使用 bind()函数将套接字绑定到本地地址和端口。

函数返回值:
如果连接建立成功,返回值为0。
如果连接建立失败,返回值为-1,并设置相应的错误码。

其他函数

在代码当中使用到的write,read,close和操作文件时的函数基本一致,这里不赘述。

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

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

相关文章

keep-alive 是 Vue 内置的一个组件,被用来缓存组件实例。

文章目录 简介注意点使用 keep-alive 有以下优缺点优点缺点 简介 keep-alive 是 Vue 内置的一个组件&#xff0c;被用来缓存组件实例。 使用 keep-alive 包裹动态组件时&#xff0c;被包裹的组件实例将会被缓存起来&#xff0c;而不会被销毁&#xff0c;直到 keep-alive 组件…

LSM零知识学习一、概念与框架机制

本文内容参考&#xff1a; LSM(Linux Security Modules)框架原理解析_lsm框架_pwl999的博客-CSDN博客 LSM相关知识及理解-布布扣-bubuko.com 一文了解Linux安全模块&#xff08;LSM&#xff09; - 嵌入式技术 - 电子发烧友网 在此特别致谢&#xff01; 一、什么是LSM LSM全…

HiFB 与Linux Framebuffer的对比

引言 HiFB和Linux Framebuffer是两种不同的图形缓冲区技术&#xff0c;它们在处理计算机图形显示方面有着重要的作用。以下是对这两种技术的简短定义&#xff1a; HiFB&#xff08;High-performance Intelligent FrameBuffer&#xff09;&#xff1a;HiFB是华为推出的一种高性…

Socket(五)

文章目录 1. 日志2. 如何记录日志 1. 日志 服务器要在无人看管的情况下运行很长时间&#xff0c;通常需要在很久以后对服务器中发生的情况进行调试&#xff0c;这很重要。由于这个原因&#xff0c;建议在存储服务器日志&#xff0c;至少要存储一段时间的日志。日志中通常希望记…

ARM微架构与程序编写

目录 1.流水线 2.指令流水线 3. 多核处理器​编辑 4. 工程搭建 4.1为Keil软件配置编译工具链 5.程序编写 5.1 数据处理指令 5.2 带标志位的加法ADC ADDS 5.3 跳转指令B\BL 5.4 单寄存器内存访问 5.5 批量寄存器内存访问 5.6 满减操作 1.流水线 2.指令流水线 3.…

算法基础学习笔记——⑭欧拉函数\快速幂\扩展欧几里得算法\中国剩余定理

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨欧拉函数 &#x1f353;求欧拉函数 : &#x1f353;筛法求欧拉函数 : ✨快速幂 ✨扩展欧几里得算法 ✨中国剩余定理 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(∩_∩)O&#…

chatgpt赋能python:Python中的倒序输出方法

Python中的倒序输出方法 在Python中&#xff0c;倒序输出是一个经常用到的操作。倒序输出可以用于字符串、列表、元组等数据类型&#xff0c;帮助我们更方便地处理数据。 字符串的倒序输出 对于字符串&#xff0c;我们可以使用字符串切片的方法倒序输出。例如&#xff0c;我…

十二、Vben之Vue3+vite跨域代理地址实现

在vue2中使用proxy进行跨域的原理是:将域名发送给本地的服务器(启动vue项目的服务,loclahost:8080),再由本地的服务器去请求真正的服务器。 代码如下: 1.在proxy中设置要访问的地址,并重写/api为空的字符串,这里如果不重写,会相当于在代理的地址上默认加了/api,所以…

chatgpt赋能python:Python中安装jieba分词器

Python中安装jieba分词器 介绍 中文分词是文本挖掘中非常重要的一个环节&#xff0c;而jieba是Python中最受欢迎的中文分词器之一。jieba分词器是基于汉语词汇库进行分词&#xff0c;并支持多种分词模式&#xff0c;可以满足不同场景的分词需求。 本文将介绍如何在Python环境…

chatgpt赋能python:Python中如何安装pip

Python中如何安装pip 什么是pip&#xff1f; pip&#xff0c;全称pip installs packages&#xff0c;是一个Python包管理工具&#xff0c;可以用来安装、升级和卸载Python包。它广泛地应用于Python社区&#xff0c;可以帮助Python开发者快速地获取和分享Python代码。 安装pi…

对比 RS232,RS422,RS485

对比 RS232,RS422,RS485 首先&#xff0c; 串口、UART口、COM口、RJ45网口、USB口是指的物理接口形式(硬件)。TTL、RS-232、RS-485、RS-422是指的电平标准(电信号)。 RS232,RS422,RS485 对比表格 通信标准RS-232RS-422RS-485工作方式单端差分差分通信线数量4 地线52 地线3节…

《深入理解计算机系统(CSAPP)》第5章 优化程序性能 - 学习笔记

写在前面的话&#xff1a;此系列文章为笔者学习CSAPP时的个人笔记&#xff0c;分享出来与大家学习交流&#xff0c;目录大体与《深入理解计算机系统》书本一致。因是初次预习时写的笔记&#xff0c;在复习回看时发现部分内容存在一些小问题&#xff0c;因时间紧张来不及再次整理…

Java中如何判断是否为闰年

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;Java经典程序设计 目录 ✨介绍 &#x1f353;引言&#xff1a;闰年的定义和在编程中的应用 &#x1f353;目的&#xff1a;介绍如何使用Java编写一个函数来判断年份是否为闰年 ✨闰年的条件 ✨提供数学原理和背景知识 &…

软考A计划-试题模拟含答案解析-卷十一

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

牛客网刷题学习SQL(三)

SQL23 统计每个学校各难度的用户平均刷题数 首先分析题目&#xff1a; 想要计算一些参加了答题的不同学校、不同难度的用户平均答题量 不同学校&#xff1a; group by 学校 不同难度&#xff1a; group by 难度 平均答题量&#xff1a;注意用户去重&#xff0c;还有指定questi…

python:绘制GAM非线性回归

作者&#xff1a;CSDN _养乐多_ 本文将介绍使用python语言绘制广义线性模型&#xff08;Generalized Additive Model&#xff0c;GAM&#xff09;非线性回归散点图和拟合曲线。并记录了计算RMSE、ubRMSE、R2、Bias的代码。 文章目录 一、GAM非线性回归详解二、代码三、计算RM…

华为OD机试真题B卷 Java 实现【统计字符】,附详细解题思路

一、题目描述 输入一行字符&#xff0c;分别统计出包含英文字母、空格、数字和其它字符的个数。 数据范围&#xff1a;输入的字符串长度满足 1 \le n \le 1000 \1≤n≤1000 。 二、输入描述 输入一行字符串&#xff0c;可以有空格。 三、输出描述 统计其中英文字符&#…

chatgpt赋能python:Python中如何空一行

Python中如何空一行 在Python编程中&#xff0c;许多情况下我们需要在输出内容的时候空出一行。今天我们将介绍如何在Python中实现空一行的方法。 方法1&#xff1a;使用print()函数 在Python中&#xff0c;我们可以使用print()函数打印空行。我们只需在print()函数中输入两…

并发编程 原子性 可见性 有序性

并发编程的三个重要特性 原子性所谓原子性是指在一次的操作或者多次操作中&#xff0c;要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断&#xff0c;要么所有的操作都不执行。可见性可见性是指&#xff0c;当一个线程对共享变量进行了修改&#xff0c;那么另…

chatgpt赋能python:Python中如何合并列表-详细教程

Python中如何合并列表 - 详细教程 在Python编程中&#xff0c;有时候需要把两个或多个列表合并成一个单一的列表&#xff0c;以便更好地进行数据处理。Python中有几种方法可以实现列表合并&#xff0c;本文将介绍其中的三种方法。 1. 使用“”符号 最常见的方法是使用“”符…