【计算机网络 - 基础问题】每日 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 表示权限问题等

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

相关推荐
长潇若雪12 分钟前
结构体(C 语言)
c语言·开发语言·经验分享·1024程序员节
feilieren15 分钟前
leetcode - 684. 冗余连接
java·开发语言·算法
Peter44723 分钟前
-bash: ./my_rename.sh: /bin/bash^M: bad interpreter: No such file or directory
开发语言·bash
The Future is mine25 分钟前
Java根据word模板导出数据
java·开发语言
ChinaDragonDreamer26 分钟前
HarmonyOS:@Watch装饰器:状态变量更改通知
开发语言·harmonyos·鸿蒙
cuiyaonan200037 分钟前
SpringBoot 下的Excel文件损坏与内容乱码问题
spring boot·后端·excel
一颗甜苞谷38 分钟前
开源一款前后端分离的企业级网站内容管理系统,支持站群管理、多平台静态化,多语言、全文检索的源码
java·开发语言·开源
星夜孤帆39 分钟前
Java面试题集锦
java·开发语言
论迹1 小时前
【Java】-- 接口
java·开发语言
DARLING Zero two♡1 小时前
关于我、重生到500年前凭借C语言改变世界科技vlog.12——深入理解指针(2)
c语言·开发语言·科技·1024程序员节