当前位置: 首页 > article >正文

Linux之socket编程(上)

目录

理解IP和端口号

 socket编程接口

 简单UDP网络小程序实现


本期我们将开始学习Linux计算机网络的相关知识。

理解IP和端口号

IP:一个IP唯一标识一个网络中的主机。

端口号:唯一标识一个主机中的一个进程。

IP+端口号我们也称作套接字

所以我们可以得出一个结论,IP+端口号可以唯一标识一个计算机中的一个进程。 

在学习Linux操作系统的时候我们也学习过进程的概念,在操作系统中我们使用pid来唯一标识计算机中的一个进程。那么套接字和进程pid冲突吗?

其实也不冲突,就如学生的身份证号和学生在学校的学号一样,都可以唯一标识学生的身份。那为什么在学校不用学生的身份证号作为学生的学号呢?其实这是为了解耦,如果真的使用身份证号作为学生的学号,那么万一有一天身份证号报废了,学生在学校的学号也就报废了。所以为了防止类似的情况出现,我们使用了学号,实现了学校与外界环境的解耦。在计算机网络中使用套接字也是类似的原因。

 socket编程接口

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)

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

// 绑定端口号 (TCP/UDP, 服务器)

int bind(int socket, const struct sockaddr *address, socklen_t address_len);

// 开始监听socket (TCP, 服务器)

int listen(int socket, int backlog);

// 接收请求 (TCP, 服务器)

int accept(int socket, struct sockaddr* address, socklen_t* address_len);

// 建立连接 (TCP, 客户端)

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

在计算机网络通信中,socket通信方式有很多种,网络套接和域间套接,但是不难发现,在socket编程中只有唯一的接口。那么针对不同的socket通信方式怎么样保证只用上述唯一的接口就能实现通信呢?

回答这个问题之前,我们先来了解一下,不同通信方式所对应的数据结构。有三种数据结构,准确来说有两种数据结构。图示如下。

struct sockaddr_in 对应网络套接,struct sockaddr_un对应域间套接。这两个数据结构可以强转成struct sockaddr通用结构,进而使得多种socket通信方式使用同一套socket接口。 

 简单UDP网络小程序实现

题设:使用socket相关api接口,实现client客户端进程发送任意消息,sever服务器端进程接收到“你好之后”,给client客户端进程返回“hello”功能的程序。

客户端代码如下。

udp_client.cc

#include <iostream>
#include <sys/types.h>
#include <cerrno>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>


int main(int argc, char *argv[])
{

    // 客户端
    // 1.创建套接字,打开网络文件
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        std::cout << "socket create error" << errno << std::endl;
        return 1;
    }
    // 客户端需要绑定ip和端口吗
    // 不需要,因为客户端是向服务器端发送数据的,所以不需要,ip和端口号,
    // 发送数据时,操作系统自动绑定


      
    //2.使用服务
    while (1)
    {
        // 给谁发
        struct sockaddr_in sever;
        sever.sin_family = AF_INET;
        sever.sin_port = htons(atoi(argv[2]));
        sever.sin_addr.s_addr = inet_addr(argv[1]);
        // 向服务器发送数据
        std::string message;
        std::cout<<"请输入# ";
        std::cin >> message;
        sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&sever, sizeof(sever));

        // 获取从服务器中返回的数据
        struct sockaddr_in tmp;
        #define NUM 1024
        char buffer[NUM];
        socklen_t len = sizeof(tmp);
        recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&tmp, &len);
        std::cout << "sever返回的数据: " << buffer << std::endl;
    }

    return 0;
}

1. 客户端进程先创建并打开一个套接字文件。客户端不用去绑定IP和端口号,因为客户端是向服务器端发送数据的,所以在发送数据时操作系统会自动为客户端进程绑定IP和端口号。

2.客户端进程获取服务,接收到了服务器端进程返回的数据。

服务器端代码如下。

udp_sever.cc

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string>

const uint16_t port = 8080;
int main()
{
    // 1.创建socket,即打开一个文件
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        std::cout << "socket create erro" << errno << std::endl;
        return 1;
    }

    // 2.绑定IP和端口号
    struct sockaddr_in local;
    // 当前socket通信方式为网络套接。
    local.sin_family = AF_INET;
    local.sin_port = htons(port);

    // 绑定IP,需要将点分十进制IP转为32位整数IP地址,与此同时还要考虑大小端存储,
    // 所以使用inet_addr()接口即可
    // 但一般在服务器端,我们一般不显式绑定IP,因为在服务器端,可以认为一个服务器
    // 可以有多个IP,所以服务器可以接收所有向该主机IP传递的数据,而不是只接收
    // 给定的一个IP收到的数据

    local.sin_addr.s_addr = INADDR_ANY; // 默认接收向该主机所有IP传递的数据
    if (bind(sock, (const sockaddr *)&local, sizeof(local)) < 0)
    {
        std::cout << "bind err" << errno << std::endl;
        return 2;
    }

    //提供服务
    #define NUM 1024
    char buffer[NUM];
    while(1)
    {
        //1.接收从客户端发送的数据
        struct sockaddr_in peer;
        socklen_t len=sizeof(peer);
        recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
        std::cout <<"#client发送的数据: "<<buffer << std::endl;
        //2.获取到数据之后,向客户端发送返回的数据
        std::string message="hello";
        sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&peer,len);
    }

    return 0;
}

1.服务器进程创建并打开一个套接字文件。

2.服务器端进程需要绑定IP和端口号,因为服务器端是接收数据的,所以必须绑定IP和端口号,只有这样,客户端进程才知道往哪个服务器进程发送数据。

3.创建服务,接收客户端进程发送的数据,并向客户端进程发送返回的数据。

运行结果如下。

运行结果符合预期。

以上便是本期的所有内容。

本期内容到此结束^_^ 


http://www.kler.cn/a/511943.html

相关文章:

  • 写作利器:如何用 PicGo + GitHub 图床提高创作效率
  • 服务器一次性部署One API + ChatGPT-Next-Web
  • PortSwigger靶场练习---第二关-查找和利用未使用的 API 端点
  • 第2章:Python TDD构建Dollar类基础
  • 第6章:Python TDD实例变量私有化探索
  • 线上工单引发的思考:Spring Boot 中 @Autowired 与 @Resource 的区别
  • Excel 技巧14 - 如何批量删除表格中的空行(★)
  • 工业现场数据实时采集:解锁工业智能化转型的关键
  • 深入理解Linux系统内存中文件结构以及缓冲区,模拟实现c语言库文件接口
  • 《重生到现代之从零开始的C++生活》—— 类和对象2
  • 【STM32-学习笔记-14-】FLASH闪存
  • 开源模型应用落地-工具使用篇-Spring AI-高阶用法(九)
  • 力扣203题—— 移除链表元素
  • ovs实现lb负载均衡
  • 外部flash烧写算法学习笔记(一)
  • Linux:EXT2文件系统
  • 分布式 IO 模块:开启药品罐装产线高效生产新纪元
  • 技术面试中的软素质技巧性答复集锦
  • 【HarmonyOS NEXT】鸿蒙三方应用跳转到系统浏览器
  • 将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(3.纯python的实惠版)
  • 第01章 分别使用DCMTK和gdcm库,解析DICOM文件系列的dicom标准数据信息
  • win32汇编环境,窗口程序中复杂列表框的应用举例
  • 家政预约小程序08服务分类
  • 【go语言】go的卸载与安装
  • 大模型迎来2025开年大作:deepseek-R1与deepseek-R1-Zero
  • 5G 核心网 相关概念快速入门