【计算机网络 - 基础问题】每日 3 题(四十八)

✍个人博客:https://blog.csdn.net/Newin2020?type=blog

📣专栏地址:http://t.csdnimg.cn/fYaBd

📚专栏简介:在这个专栏中,我将会分享 C++ 面试中常见的面试题给大家~

❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

📝推荐参考地址:https://www.xiaolincoding.com/(这个大佬的专栏非常有用!)

142. 针对 TCP 应该如何 Socket 编程?

  • 服务端和客户端初始化 socket,得到文件描述符;
  • 服务端调用 bind,将 socket 绑定在指定的 IP 地址和端口;
  • 服务端调用 listen,进行监听;
  • 服务端调用 accept,等待客户端连接;
  • 客户端调用 connect,向服务端的地址和端口发起连接请求;
  • 服务端 accept 返回用于传输的 socket 的文件描述符;
  • 客户端调用 write 写入数据;服务端调用 read 读取数据;
  • 客户端断开连接时,会调用 close,那么服务端 read 读取数据的时候,就会读取到了 EOF,待处理完数据后,服务端调用 close,表示连接关闭。

这里需要注意的是,服务端调用 accept 时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。

所以,监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作监听 socket,一个叫作已完成连接 socket。

成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。

套接字函数:

cpp 复制代码
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>  // 包含了这个头文件,上面两个就可以省略

int socket(int domain, int type, int protocol);
    - 功能:创建一个套接字
    - 参数:
        - domain: 协议族 
            AF_INET : ipv4 
            AF_INET6 : ipv6
            AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信)
        - type: 通信过程中使用的协议类型 
            SOCK_STREAM : 流式协议 
            SOCK_DGRAM : 报式协议
        - protocol : 具体的一个协议。一般写0
            SOCK_STREAM : 流式协议默认使用TCP
            SOCK_DGRAM : 报式协议默认使用UDP
    - 返回值:
        - 成功:返回文件描述符,操作的就是内核缓冲区。
        - 失败:-1

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命名
    - 功能:绑定,将fd和本地的IP + 端口进行绑定
    - 参数:
        - sockfd : 通过socket函数得到的文件描述符
        - addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息
        - addrlen : 第二个参数结构体占的内存大小

int listen(int sockfd, int backlog);        // /proc/sys/net/core/somaxconn
    - 功能:监听这个socket上的连接
    - 参数:
        - sockfd : 通过socket()函数得到的文件描述符
        - backlog : 未连接的和已经连接的和的最大值, 5

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    - 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
    - 参数:
        - sockfd : 用于监听的文件描述符
        - addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
        - addrlen : 指定第二个参数的对应的内存大小
    - 返回值:
        - 成功 :用于通信的文件描述符
        - -1 : 失败

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    - 功能:客户端连接服务器
    - 参数:
        - sockfd : 用于通信的文件描述符
        - addr : 客户端要连接的服务器的地址信息
        - addrlen : 第二个参数的内存大小
    - 返回值:成功 0, 失败 -1

ssize_t write(int fd, const void *buf, size_t count);        // 写数据
ssize_t read(int fd, void *buf, size_t count);        // 读数据

// TCP数据读写专用
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);
        - send往sockfd上写入数据,成功返回实际写入的数据长度,失败返回-1并设置errno

// UDP数据读写专用
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, struct sockaddr* dest_addr, socklen_t addrlen);

案例:TCP 通信实现

服务器端 server.c

cpp 复制代码
// TCP 通信的服务器端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.创建socket(用于监听的套接字)
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // inet_pton(AF_INET, "192.168.193.128", saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
    saddr.sin_port = htons(9999);
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.监听
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 4.接收客户端连接
    struct sockaddr_in clientaddr;
    int len = sizeof(clientaddr);
    int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
    
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }

    // 输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is %s, port is %d\n", clientIP, clientPort);

    // 5.通信
    char recvBuf[1024] = {0};
    while(1) {
        // 获取客户端的数据
        int num = read(cfd, recvBuf, sizeof(recvBuf));
        if(num == -1) {
            perror("read");
            exit(-1);
        } else if(num > 0) {
            printf("recv client data : %s\n", recvBuf);
        } else if(num == 0) {
            // 表示客户端断开连接
            printf("clinet closed...");
            break;
        }

        char * data = "hello,i am server";
        // 给客户端发送数据
        write(cfd, data, strlen(data));
    }
   
    // 关闭文件描述符
    close(cfd);
    close(lfd);

    return 0;
}

客户端 client.c

cpp 复制代码
// TCP通信的客户端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.创建套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.193.128", &serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9999);
    int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    if(ret == -1) {
        perror("connect");
        exit(-1);
    }
    
    // 3. 通信
    char recvBuf[1024] = {0};
    while(1) {

        char * data = "hello,i am client";
        // 给客户端发送数据
        write(fd, data , strlen(data));

        sleep(1);
        
        int len = read(fd, recvBuf, sizeof(recvBuf));
        if(len == -1) {
            perror("read");
            exit(-1);
        } else if(len > 0) {
            printf("recv server data : %s\n", recvBuf);
        } else if(len == 0) {
            // 表示服务器端断开连接
            printf("server closed...");
            break;
        }

    }

    // 关闭连接
    close(fd);

    return 0;
}

143. socket 底层的关键字你有使用过吗,协议怎么设计的?

Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部。Socket 是对 TCP/IP 协议的封装,Socket 本身并不是协议,而是一个调用接口(API),通过 Socket,我们才能使用 TCP/IP 协议。

在 socket 底层中,关键字有:socket_state(socket 状态)、type(socket 类型)、flags(socket 的标志位)、wq(等待队列指针)、file(与 socket 关联的文件指针)、sk(套接字的核心,面向底层网络具体协议)等。这些关键字都是用来描述和控制 socket 的行为和状态的。

所谓的协议族(Protocol Family),就是一组协议(多个协议)的统称。最常用的是 TCP/IP 协议族,它包含了 TCP、IP、UDP、Telnet、FTP、SMTP 等上百个互为关联的协议,由于 TCP、IP 是两种常用的底层协议,所以把它们统称为 TCP/IP 协议族,用作数据传输。

至于协议的设计,通常是基于 TCP/IP 协议族来进行的。每一种协议都有其特定的功能和应用场景,比如 TCP 协议主要负责数据的可靠传输,而 UDP 协议则主要用于快速数据传输。

144. Socket 函数返回值代表什么意思?

在编程中,socket 函数通常用于在网络编程中创建套接字(sockets),它们是通信的基础设施,可以用来发送和接收数据。具体来说,socket 函数在很多编程语言中都有实现,如 Python、C、C++ 等。每个编程语言可能有一些微小的语法差异,但基本的函数结构和返回值通常是一样的。

在 socket 函数中,返回值通常用于指示操作是否成功。如果返回值为 0 或正值,则表示操作成功。如果返回值为负值,则表示发生了错误。具体错误信息可以通过操作系统提供的错误处理函数或 socket 函数的特定错误返回值来获取。

通常,以下是一些可能的返回值解释:

  • 返回值 >0:表示操作成功
  • -1(没有具体的错误码):表示请求失败,但是因为无法识别具体错误(如找不到 socket 地址等),可能需要查阅特定的文档或者运行更多的错误检查
  • 其他负数:表示具体的错误类型,比如 ENOMEM 表示内存不足,EACCES 表示权限问题等

在编程时,应仔细查看函数的文档或示例代码以了解其具体含义和用法。

相关推荐
zy张起灵19 分钟前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
冰帝海岸1 小时前
01-spring security认证笔记
java·笔记·spring
----云烟----2 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024062 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
小二·2 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic2 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it2 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
武子康2 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
qq_17448285753 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
转世成为计算机大神3 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式