Linux的socket通信

关于套接字通信定义如下:

套接字对应程序猿来说就是一套网络通信的接口,使用这套接口就可以完成网络通信。网络通信的主体主要分为两部分:客户端和服务器端。在客户端和服务器通信的时候需要频繁提到三个概念:IP、端口、通信数据,下面介绍一下需要注意的一些细节问题。

引用来源于爱编程的大丙

服务器端的套接字通信流程:

(1)建立套接字,这个套接字就是一个文件描述符。


  • int lfd = socket();
  • 函数原型如下:
  • // 创建一个套接字
    int socket(int domain, int type, int protocol);

参数:
domain: 使用的地址族协议

  • AF_INET: 使用IPv4格式的ip地址
  • AF_INET6: 使用IPv4格式的ip地址
    type:
  • SOCK_STREAM: 使用流式的传输协议
  • SOCK_DGRAM: 使用报式(报文)的传输协议
  • protocol: 一般写0即可, 使用默认的协议
  • SOCK_STREAM: 流式传输默认使用的是tcp
  • SOCK_DGRAM: 报式传输默认使用的udp
    返回值:
  • 成功: 可用于套接字通信的文件描述符
  • 失败: -1
    函数的返回值是一个文件描述符,通过这个文件描述符可以操作内核中的某一块内存,网络通信是基于这个文件描述符来完成的。
c 复制代码
    // 1. 创建监听的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1)
    {
        perror("socket");
        exit(0);
    }

(2)绑定:将得到的监听的文件描述符和本地的IP 端口进行绑定


// 将文件描述符和本地的IP与端口进行绑定

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


  • 返回值:成功返回0,失败返回-1
c 复制代码
    // 2. 将socket()返回值和本地的IP端口绑定到一起
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
     //系统存储的是小端端口,所以需要转换一下
    addr.sin_port = htons(10000);   // 大端端口,这里因为网络通信是使用的大端端口,
   
    // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址
    // 这个宏可以代表任意一个IP地址
    // 这个宏一般用于本地的绑定操作
    addr.sin_addr.s_addr = INADDR_ANY;  // 这个宏的值为0 == 0.0.0.0,因为这个都是0000,不需要大小端转换
    int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
    {
        perror("bind");
        exit(0);
    }

(3) 设置监听


// 给监听的套接字设置监听

int listen(int sockfd, int backlog);


参数:

  • sockfd: 文件描述符, 可以通过调用socket()得到,在监听之前必须要绑定 bind()
  • backlog: 同时能处理的最大连接要求,最大值为128
    返回值:
  • 函数调用成功返回0,调用失败返回 -1
c 复制代码
   // 3. 设置监听
    ret = listen(lfd, 128);
    if(ret == -1)
    {
        perror("listen");
        exit(0);
    }

(4)阻塞并等待连接
会得到一个新的文件描述符(通信的)


// 等待并接受客户端的连接请求, 建立新的连接,

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


返回值:函数调用成功,得到一个文件描述符, 用于和建立连接的这个客户端通信,调用失败返回 -1

c 复制代码
    // 4. 阻塞等待并接受客户端连接
    struct sockaddr_in cliaddr;
    int clilen = sizeof(cliaddr);
    int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &clilen);
    if(cfd == -1)
    {
        perror("accept");
        exit(0);
    }

(5)如果成功之后打印一条消息(可有可无)

c 复制代码
    // 打印客户端的地址信息
    char ip[24] = {0};
    printf("客户端的IP地址: %s, 端口: %d\n",
           inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)),
           ntohs(cliaddr.sin_port));

(6)开始通信

c 复制代码
    // 5. 和客户端通信
    while(1)
    {
        // 接收数据
        char buf[1024];
        memset(buf, 0, sizeof(buf));
        int len = read(cfd, buf, sizeof(buf));
        if(len > 0)
        {
            printf("客户端say: %s\n", buf);
            write(cfd, buf, len);
        }
        else if(len  == 0)
        {
            printf("客户端断开了连接...\n");
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }

server.c

c 复制代码
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

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

    // 2. 将socket()返回值和本地的IP端口绑定到一起
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);   // 大端端口
    // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址
    // 这个宏可以代表任意一个IP地址
    // 这个宏一般用于本地的绑定操作
    addr.sin_addr.s_addr = INADDR_ANY;  // 这个宏的值为0 == 0.0.0.0
    int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
    {
        perror("bind");
        exit(0);
    }

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

    // 4. 阻塞等待并接受客户端连接
    struct sockaddr_in cliaddr;
    int clilen = sizeof(cliaddr);
    int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &clilen);
    if(cfd == -1)
    {
        perror("accept");
        exit(0);
    }
    // 打印客户端的地址信息
    char ip[24] = {0};
    printf("客户端的IP地址: %s, 端口: %d\n",
           inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)),
           ntohs(cliaddr.sin_port));

    // 5. 和客户端通信
    while(1)
    {
        // 接收数据
        char buf[1024];
        memset(buf, 0, sizeof(buf));
        int len = read(cfd, buf, sizeof(buf));
        if(len > 0)
        {
            printf("客户端say: %s\n", buf);
            write(cfd, buf, len);
        }
        else if(len  == 0)
        {
            printf("客户端断开了连接...\n");
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }

    close(cfd);
    close(lfd);

    return 0;
}

client.c

c 复制代码
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

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

    // 2. 连接服务器
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);   // 大端端口
    inet_pton(AF_INET, "10.0.2.15", &addr.sin_addr.s_addr);

    int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
    {
        perror("connect");
        exit(0);
    }

    // 3. 和服务器端通信
    int number = 0;
    while(1)
    {
        // 发送数据
        char buf[1024];
        sprintf(buf, "你好, 服务器...%d\n", number++);
        write(fd, buf, strlen(buf)+1);
        
        // 接收数据
        memset(buf, 0, sizeof(buf));
        int len = read(fd, buf, sizeof(buf));
        if(len > 0)
        {
            printf("服务器say: %s\n", buf);
        }
        else if(len  == 0)
        {
            printf("服务器断开了连接...\n");
            break;
        }
        else
        {
            perror("read");
            break;
        }
        sleep(1);   // 每隔1s发送一条数据
    }

    close(fd);

    return 0;
}
相关推荐
不染尘.7 分钟前
Linux的基本管理及命令(上)
linux·windows·ssh
米高梅狮子11 分钟前
12. SELinux 加固 Linux 安全
linux·运维·安全
weixin_4624462311 分钟前
【原创实践】Windows 和 Linux 下使用 Python 3.10 搭建 PaddleOCRVL 识别图片并100%还原表格
linux·windows·python·飞浆
Sumlll_8 小时前
Ubuntu系统下QEMU的安装与RISC-V的测试
linux·ubuntu·risc-v
猫头虎9 小时前
2025最新OpenEuler系统安装MySQL的详细教程
linux·服务器·数据库·sql·mysql·macos·openeuler
晚风吹人醒.10 小时前
SSH远程管理及访问控制
linux·运维·ssh·scp·xshell·访问控制·远程管理
Uncertainty!!12 小时前
Linux多用户情况下个别用户输入密码后黑屏
linux·远程连接
necessary65312 小时前
使用Clion查看linux环境中的PG源码
linux·运维·服务器
小猪佩奇TONY13 小时前
Linux 内核学习(14) --- linux x86-32 虚拟地址空间
linux·学习
Lam㊣13 小时前
Centos 7 系统docker:更换镜像源
linux·docker·centos