套接字Socket学习笔记

2 篇文章 0 订阅

参考博客:
https://www.cnblogs.com/ch2020/p/12518856.html
https://blog.csdn.net/wzyaiwl/article/details/83147632
https://subingwen.cn/linux/

1.什么是Socket

Socket 套接字由远景研究规划局(Advanced Research Projects Agency, ARPA)资助加里福尼亚大学伯克利分校的一个研究组研发。其目的是将 TCP/IP 协议相关软件移植到 UNIX 类系统中。

  • 下面引用维基百科对Socket的解释:

Berkeley Socket是Internet 套接字和Unix 域套接字的应用程序编程接口(API) ,用于进程间通信(IPC)。它通常被实现为可链接模块的库。它起源于 1983 年发布的4.2BSD Unix操作系统。

我们从维基百科的解释可以发现,套接字最开始来源于福尼亚大学伯克利分校,所以一开始Socket又被成为Berkeley Socket(BSD)。另外,套接字分为两种:

  1. 基于文件类型的套接字

Unix中一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程在同一机器,可以通过访问同一个文件系统间接完成通信。

  1. 基于网络类型的套接字

是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

注:本文以下所讲的都将基于网络套接字

2. 什么是Web Socket

假设我们当前是客户端,我们想要看抖音直播,抖音的服务器一直是开着的对吧;但是它怎么知道我们什么时候去脸上的它的服务器的呢,就是因为它的服务器一直处于监听状态,当我们打开抖音的时候会去向抖音的服务器进行绑定;然后它会返回数据,然后我们就能美滋滋的看小姐姐直播啦!
在这里插入图片描述

  • 套接字(socket)为通信的端点,每个套接字由一个 IP 地址和一个端口号组成。通过网络通信的每对进程需要使用一对套接字,即每个进程各有一个。
  • 通常,套接字采用客户机-服务器架构。服务器通过监听指定端口,来等待客户请求。服务器在收到请求后,接受来自客户套接字的连接,从而完成连接。
  • 套接字用(IP地址:端口号)表示,区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。

这个应该是很容易理解的,我们平时使用浏览器浏览网页就是IP:Port的形式,只不过IP地址通过了DNS映射到了域名上,而Port是在HTTPS中默认端口是443【被省略了】

3.套接字分类

通常情况下程序员接所接触到的套接字(Socket)为两类:
(1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用;
(2)数据报式套接字(SOCK_DGRAM):一种无连接的 Socket,对应于无连接的 UDP 服务应用。

从用户的角度来看,SOCK_STREAM、SOCK_DGRAM 这两类套接字似乎的确涵盖了 TCP/IP 应用的全部,因为基于 TCP/IP 的应用,从协议栈的层次上讲,在传输层的确只可能建立于 TCP 或 UDP 协议之上,而 SOCK_STREAM、SOCK_DGRAM 又分别对应于 TCP 和 UDP,所以几乎所有的应用都可以用这两类套接字实现。
在这里插入图片描述

4.套接字的C实现

参考了大丙哥的实现【推荐】

服务端Server代码如下

// 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
//    inet_pton(AF_INET, "192.168.237.131", &addr.sin_addr.s_addr);
    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代码如下:

// 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, "101.34.171.156", &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;
}

效果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值