编写一个最简单的Linux服务端和客户端程序

news2024/12/25 1:00:46

2023年8月3日,周四下午

这篇文章我从下午开始写了几个小时,

这篇文件基本总结了我今天学到的知识,

在写这篇文章的过程中灵感不断涌现、想明白了很多知识点,非常酣畅淋漓。

什么叫做深度学习?这就是深度学习!深度学习就是打破砂锅问到底,就是不断提出问题和解决问题。

有点飘了,感觉自己也能写书和出书了,哈哈哈。

其实我感觉自己还没写完,还有些疑惑没解决,以后有空再更新。


目录

  • 纯享版
  • 功能
  • 服务端程序
  • 客户端程序
  • 通俗版
  •  服务端程序
  • 客户端程序
  • 讲解版
  • 服务端程序
  • 为什么要设置服务器端口号?
  • 为什么不需要设置服务端的IP地址?
  • INADDR_ANY是什么意思?
  • 怎么获取到客户端的IP地址和端口号?
  • ​编辑
  • 怎么理解listen(serv_sock,5)?
  • 客户端程序

纯享版

功能

服务端负责发送Hello给客户端,

客户端负责把Hello接收并打印出来。

服务端程序

#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
int main(){

    int serv_sock=socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in serv_addr;
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_addr.sin_port=htons(9990);

    bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));

    listen(serv_sock,5);

    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size=sizeof(clnt_addr);
    int clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);

    write(clnt_sock,"Hello",sizeof("Hello"));

    close(clnt_sock);
    close(serv_sock);
    return 0;
}

客户端程序

#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<string.h>

int main(){
    
    int sock=socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in serv_addr;
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    serv_addr.sin_port=htons(9990);

    connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));

    char message[30];
    read(sock,message,sizeof(message)-1);
    printf("%s\n",message);

    close(sock);
    return 0;
}

通俗版

 服务端程序

#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
int main(){
    
    //创建一台手机
    int serv_sock=socket(AF_INET,SOCK_STREAM,0);
    
    //创建一张电话卡
    struct sockaddr_in serv_addr;
    
    //运营商负责给电话卡填写信息
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_addr.sin_port=htons(9990);
    
    //把电话卡插到手机里面
    bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
    
    //设置手机来电话时提醒
    listen(serv_sock,5);
    
    //再创建一张电话卡,用来存储打电话过来的手机的电话卡信息,比如手机号
    struct sockaddr_in clnt_addr;

    socklen_t clnt_addr_size=sizeof(clnt_addr);
    
    //如果有电话打来就把来电的电话卡信息存储到clnt_addr电话卡里面
    int clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);
    
    //按照clnt_addr电话卡的信息给这个来电的手机发送一个字符串"Hello"
    write(clnt_sock,"Hello",sizeof("Hello"));

    close(clnt_sock);
    close(serv_sock);
    return 0;
}

客户端程序

#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<string.h>

int main(){
    
    //创建一部手机
    int sock=socket(AF_INET,SOCK_STREAM,0);
    
    //创建一张电话卡卡
    struct sockaddr_in serv_addr;
    
    //运营商负责填写电话卡信息
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//负责确定打给谁
    serv_addr.sin_port=htons(9990);//负责确定打给谁
    
    //拨打电话
    connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));

    char message[30];
    //读取收到的"Hello"字符串
    read(sock,message,sizeof(message)-1);
    printf("%s\n",message);

    close(sock);
    return 0;
}

讲解版

服务端程序

#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
int main(){

    int serv_sock=socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in serv_addr;
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//INADDR_ANY表示可以接受来自任意IP地址的连接
    serv_addr.sin_port=htons(9990);//设置服务器端口号

    bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));

    listen(serv_sock,5);

    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size=sizeof(clnt_addr);
    int clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);

    write(clnt_sock,"Hello",sizeof("Hello"));

    close(clnt_sock);
    close(serv_sock);
    return 0;
}

为什么要设置服务器端口号?

端口号是一个16位的无符号整数,它可以唯一地标识服务器上的不同服务。

因为服务器在互联网中的IP地址是唯一的,但是服务器上运行着很多服务或者说程序,有的可能是用来处理web网页请求,有的可能是处理远程发来的数据库请求的。而由上面这个服务端代码建立的服务或者说程序只是服务器上众多服务或者说程序中的一个,那么当用本文的客户端代码创建的客户端程序通过IP地址找到这个服务器时,客户端程序怎么知道服务器上的运行着的数量众多的程序中哪个是用本文的服务端代码创建的服务端程序呢?通过设置具有唯一性的用来标识服务器上的不同服务的服务器端口号就能完美的解决这个问题,所以要设置服务器端口号。

进一步的说,用本文的客户端代码创建的客户端程序只要知道运行着用本文的服务端代码创建的服务端程序的服务器在互联网中的IP地址和这个服务端程序在服务器中的端口号,就能在茫茫的互联网中,精确的找到用本文的服务端代码创建的服务端程序。

在计算机网络中,客户端通过指定目标服务器的IP地址和端口号来与服务器建立连接。当客户端向服务器发送请求时,服务器会根据目标端口号识别请求,并将其路由到相应的服务程序。

通过设置端口号,可以使服务器上的不同服务在不同的端口上监听连接请求。这样,当客户端想要连接到特定的服务时,只需要指定相应的端口号即可。例如,Web服务器通常监听80端口,SMTP服务器通常监听25端口。

总而言之,设置服务器端口号是为了标识服务器上的不同服务,使客户端可以通过指定端口号与特定服务建立连接。

如果没有IP地址,怎么在互联网的几千万台计算机中,找到运行着用本文的服务端代码创建的服务端程序的计算机呢?如果没有端口,怎么在运行着众多程序的计算机中,用本文的服务端代码创建的服务端程序呢?

这就是为什么要设置端口和IP地址。

为什么不需要设置服务端的IP地址?

以后有空再写....

INADDR_ANY是什么意思?

INADDR_ANY表示可以接受来自任意IP地址的连接。

如果你把INADDR_ANY替换成成127.0.0.1,那么只会接收IP地址为127.0.0.1的客户端的连接请求

怎么获取到客户端的IP地址和端口号?

在上面的代码中,clnt_addr 是 sockaddr_in 类型的结构体,用于保存客户端的地址信息

accept函数会自动为我们填充 clnt_addr 结构体,其中包含:

  • clnt_addr.sin_addr.s_addr 客户端的IP地址
  • clnt_addr.sin_port 客户端的端口号

所以,在上面的代码中的accept函数下面添加如下代码就可以获取到客户端的IP地址和端口号:

char* ip = inet_ntoa(clnt_addr.sin_addr);
printf("Client IP: %s\n", ip);

int port = ntohs(clnt_addr.sin_port);        
printf("Client port: %d\n", port);

值得注意的是,客户端程序的端口号和服务端设置的端口号是不一样的

怎么理解listen(serv_sock,5)?

通过设置 listen的第2个参数,可以控制连接队列中允许等待的最大连接数。如果连接队列已满,新的连接请求将被拒绝,直到有连接请求被处理并从队列中移出。

对于这个连接队列,我想说说自己的理解:

我认为连接队列在上面这个服务端程序是不可能满的,因为上面这个服务端程序没有创建新的线程或进程来处理连接请求。我认为只有使用了线程或者进程才可能会让连接队列满,之后有空的话,我会写一些程序来说明这个。

客户端程序

#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<string.h>

int main(){
    
    int sock=socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in serv_addr;
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); //指明要连接的服务器的地址是什么
    serv_addr.sin_port=htons(9990);//指明要连接服务器的哪个端口

    connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));

    char message[30];
    read(sock,message,sizeof(message)-1);
    printf("%s\n",message);

    close(sock);
    return 0;
}

这部分有空再更新....

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

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

相关文章

仅一个月作品获推荐550.51w,视频号内容创作出现新趋势

7月初&#xff0c;视频号上线直播切片授权功能&#xff0c;创作者经授权可对直播内容进行二次加工上传至短视频平台&#xff0c;对新进入的普通创作者来说&#xff0c;切片授权无疑是一种很好的起号策略&#xff0c;吸引了大量用户。 视频号不断丰富内容生态&#xff0c;大批创…

Flink作业调度的9种状态

1.什么是作业调度 Flink 通过 Task Slots 来定义执行资源。每个 TaskManager 有一到多个 task slot&#xff0c;每个 task slot 可以运行一条由多个并行 task 组成的流水线。 这样一条流水线由多个连续的 task 组成&#xff0c;比如并行度为 n 的 MapFunction 和 并行度为 n 的…

WPF上位机8——C#与MySQL

ADO.NET 数据库连接 数据插入、删除、更改 数据查询 带单个参数 带多个参数 基于特性反射DAO通用操作库 ORM对象关系映射 数据库中表映射为实体类 调用ORM框架中提供的新增方法&#xff1a;构建sql语句 创建枚举 根据枚举类型创建sql语句 调用 实体限定 生成参数列表 生成插入…

【项目 进程13】2.28共享内存(1) 2.29共享内存(2)

文章目录 2.28共享内存&#xff08;1&#xff09;共享内存&#xff08;效率最高&#xff0c;比内存映射更高。因为内存映射还需一个文件做载体&#xff09;共享内存使用步骤共享内存操作函数头文件 2.29共享内存&#xff08;2&#xff09;共享内存相关问题共享内存和内存映射的…

MongoDB文档-基础使用-在客户端(dos窗口)/可视化工具中使用MongoDB基础语句

阿丹&#xff1a; 本文章将描述以及研究mongodb在客户端的基础应用以及在spring-boot中整合使用mongodb来完成基本的数据增删改查。 先放官方的文章 MongoDB CRUD操作 - MongoDB-CN-Manual 本文章分为&#xff1a; 在客户端&#xff08;dos窗口&#xff09;/可视化工具中使用…

工作流管理系统如何提升生产力

工作流管理工具通过提供无缝沟通和简化流程来提高团队的生产力。如果部署得当&#xff0c;工作流管理可以长期获得回报。 以下是工作场所管理系统如何提高员工的生产力&#xff1a; 1、无需更多的状态更新会议 工作流管理系统使您的员工无需跟踪电子邮件和讨论线程来评估项目…

【Pytorch】下载CIFAR10数据集报错: urllib.error.URLError: <urlopen error name: https>

在使用Pytorch 下载CIFAR10的时候&#xff0c;遇到一个报错&#xff0c; 可能是网络特别慢导致的&#xff0c;一般情况下都会遇到这个报错。 解决办法&#xff1a; 1、到官网直接下载这个压缩包&#xff0c;解压。 http://www.cs.utoronto.ca/~kriz/cifar.html 解压后&#x…

一套ai绘图软件教程帮你打开创作新篇章

在一个寂静的艺术工作室里&#xff0c;住着一个渴望成为画家的年轻人&#xff0c;名叫艾米。她梦想能够运用自己的创造力和手艺&#xff0c;创作出令人叹为观止的艺术作品。然而&#xff0c;面对空白的画布&#xff0c;她感到无从下手&#xff0c;不知道该从何处开始。每次她试…

数据可视化(七)常用图表的绘制

1. #seaborn绘制常用图表 #折线图 #replot&#xff08;x&#xff0c;y&#xff0c;kind&#xff0c;data&#xff09; #lineplot&#xff08;x&#xff0c;y&#xff0c;data&#xff09; #直方图 #displot&#xff08;data&#xff0c;rug&#xff09; #条形图 #barplot&…

MONAI的测试与使用(一)

MONAI的测试与使用&#xff08;一&#xff09; 一.Transform 的分类二 普通变换和字典变换的联系与区别三 加载与显示图像 一.Transform 的分类 具体API函数请参考文档&#xff1a;https://docs.monai.io/en/latest/transforms.html 二 普通变换和字典变换的联系与区别 普通变…

DAY2,C高级(shell脚本的使用)

1.今日思维导图&#xff1b; 2.递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位&#xff1b; #include<my_head.h>void Output(int num) {if(num 0)return;Output(num/10);printf("%d ",num%10);}int main(int argc, const char *argv[]) {in…

软件测试自动化selenuim的常用方法和属性总结

selenuim其实主要就是使用webdriver实例对象的方法和属性。 常用属性 1 driver.current_url 当前网页的请求地址 2 driver.current_window_handle 句柄&#xff0c;用于页面切换 3 driver.page_source 网页源代码 4 driver.title 网站的title&#xff0c;tab栏上显示的内容…

『Linux学习笔记』Linux服务器硬件配置查看

Linux服务器硬件配置查看 文章目录 一. 操作系统1.1. 发行版本1.2. 查看内核版本 二. CPU2.1. CPU统计信息2.2. CPU型号2.3. 查看物理CPU数目2.4. 每个物理CPU的核数2.5. 查看逻辑CPU的个数 三. GPU四. 内存4.1. 内存使用情况4.2. 内存硬件情况4.3. 查看内存使用信息 五. 硬盘…

幂等性问题 —— 如何防止重复创建订单

幂等性&#xff1a;对接口的多余重复调用的结果和单次调用的结果一致。 一、需要幂等性的场景 二、幂等性实现方案 核心思想&#xff1a;首先要根据业务判断什么是重复相同的请求&#xff0c;然后相同的请求都携带或生成一个唯一的识别码&#xff0c;这样在服务端就可以进行判…

JVM调优工具详解以及实战

准备 事先启动一个web应用程序&#xff0c;用jps查看进程id&#xff0c;接着用各种jdk自带的命令优化应用 Jmap jmap -histo 6160 #查看历史生成的实例 jmap -histo:live 6160 #查看当前存活的实例&#xff0c;执行过程中可能会触发一次full gc jmap -histo:live 6160 &…

关于数据仓库那些事 - 数据质量和数仓主题

数字化时代&#xff0c;数据成为了人类社会中必不可少的元素&#xff0c;也变成了现代企业经营管理中的关键。借助海量的数据&#xff0c;企业进行了深层次的数字化改革&#xff0c;把数据当成了企业发展的核心&#xff0c;但无效的数据即使规模再大&#xff0c;也对企业没有意…

EasyExcel工具类,一键实现表格导入导出,有无对应实体类均可

EasyExcel工具类&#xff0c;一键实现表格导入导出&#xff0c;有无对应实体类均可 Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存&#xff0c;poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题&#xff0c;但…

linux du命令解析(递归计算文件子目录大小)(计算大小)(计算容量)

文章目录 du命令简介用法常用选项示例 文档原 中文选项详细解释示例递归统计某个目录下所有文件大小&#xff08;不足单位会向上取整&#xff09;&#xff08;注意&#xff1a;可能会将目录大小也统计进去&#xff0c;目录大小为4096字节4kb&#xff1f;&#xff09; du命令使用…

【第一阶段】kotlin语言的String模板

1.在Java中拼接字符串使用的是“” 2.在kotlin中使用"${}" 3.kotlin语言中if是表达式&#xff0c;更灵活 fun main() {val city"西安"val time24//java中写法println("我在"city"玩了"time"小时")//kotlin中写法&#xff0…

2023年华数杯C题思路

c题 母亲身心健康对婴儿成长的影响 母亲是婴儿生命中最重要的人之一,她不仅为婴儿提供营养物质和身体保护,还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况&#xff0c;如抑郁、焦虑压力等&#xff0c;可能会对婴儿的认知、情感、社会行为等方面产生负面影响。压力…