Linux网络编程9——UDP 和 本地/网络套接字

学习视频链接

19-本地套接字和网络套接字比较_bilibili_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1iJ411S7UA?p=105&vd_source=0471cde1c644648fafd07b54e303c905

目录

一、UDP 和 TCP 的优缺点

二、UDP 实现的 C/S 模型

2.1 通信过程

2.2 函数讲解

2.3 代码

三、本地套接字和网络套接字

3.1 进程间通信方式 (IPC)

3.2 本地套接字对比网络 TCP 


一、UDP 和 TCP 的优缺点

1、TCP 

面向连接的,可靠数据包传输。对于不稳定的网络层,采取完全弥补的通信方式。丢包重传。

优点:稳定 (数据流量稳定、速度稳定、顺序)

缺点:传输速度慢。相率低。开销大

使用场景:数据的完整型要求较高,不追求效率。大数据传输、文件传输

2、UDP

无连接的,不可靠的数据报传递。对于不稳定的网络层,采取完全不弥补的通信方式。默认还原网络状况

优点:传输速度块。相率高。开销小

缺点:不稳定 (数据流量。速度。顺序)

使用场景:对时效性要求较高场合。稳定性其次。游戏、视频会议、视频电话。腾讯、华为、阿里--- 应用层数据校验协议, 弥补 udp 的不足

二、UDP 实现的 C/S 模型

2.1 通信过程

recv() / send() 只能用于 TCP 通信。替代 read、write

accpet():--- Connect();  ---被舍弃

server:

        lfd = socket(AF_INET, STREAM, 0);

        SOCK_DGRAM  ---报式协议

        bind();

        listen(); --- 可有可无

        while(1) {

                read(cfd, buf, sizeof)  ---被替换  ---recvfrom()  ---涵盖accept传出地址结构体

                小 -- 大

                write():---被替换--- sendto()

        }

        close();

client:

        connfd = socket(AF_INET, SOCK_DGRAI, 0);

        sendto(‘服务器的地址结构’, 地址结构大小)

        recvfrom()

        写到屏幕

        close();

2.2 函数讲解

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

        sockfd: lfd

        buf: 缓冲区地址

        len: 缓神区大小

        flags: 0

        src_addr: (struct sockaddr *) &addr 传出。对端地址结构

        addrlen: 传入传出

返回值: 成功接收数据字节数。失败: -1 errn。 0: 对端关闭

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

        sockfd: 套接字

        buf: 存储数据的缓冲区

        len: 数据长度

        flags: 0

        src_addr: (struct sockaddr *) &addr 传入。目标地址结构

        addrlen: 地址结构长度

返回值:成功接收数据字节数。失败 -1 errno

2.3 代码

server.c

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

#define SERV_PORT 8000

int main(void)
{
    struct sockaddr_in serv_addr, clie_addr;
    socklen_t clie_addr_len;
    int sockfd;
    char buf[BUFSIZ];
    char str[INET_ADDRSTRLEN];
    int i, n;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(SERV_PORT);
    
    bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

    printf("Accepting connections ...\n");
    while (1) {
        clie_addr_len = sizeof(clie_addr);
        n = recvfrom(sockfd, buf, BUFSIZ, 0, (struct sockaddr *)&clie_addr, &clie_addr_len);
        if (n == -1) {
            perror("recvfrom error");
        }

        printf("received from %S at PORT %d\n", 
                inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)), 
                ntohs(clie_addr.sin_port));
        
        for(i = 0; i < n; i++) {
            buf[i] = toupper(buf[i]);
        }
        
        n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
        if (n == -1) {
            perror("sendto error");
        }
    }

    close(sockfd);
    
    return 0;
}

 client.c

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

#define SERV_PORT 8000  

int main(int argc, char *argv[])  
{  
    struct sockaddr_in servaddr;  
    int sockfd, n;  
    char buf[BUFSIZ];  

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);  

    bzero(&servaddr, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);  
    servaddr.sin_port = htons(SERV_PORT);  

    bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));  
 
    while (fgets(buf, BUFSIZ, stdin) != NULL) {  
        n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));  
        if (n == -1)  
            perror("sendto error");  

        n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0);         //NULL:不关心对端信息  
        if (n == -1)  
            perror("recvfrom error");  

        write(STDOUT_FILENO, buf, n);  
    }  

    close(sockfd);  

    return 0;  
}  

三、本地套接字和网络套接字

3.1 进程间通信方式 (IPC)

pipe:易用性是最强的

fifo:没有血缘关系进程之间的通信

mmap:没有血缘关系进程之间的通信,可以反复读取

信号:开销最小

本地套接字 (domain):稳定性最好的(CS 模型)

3.2 本地套接字对比网络 TCP 

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

参数:

domin: AF_INET 变为 AF_UNIX/AF_LOCAL

type: SOCK_STREAM/SOCK_DGRAM 都可以

2、地址结构 socke_in -> sockaddr_un

struct sockaddr_in srv_addr; -> struct sockaddr_un srv_addr;

srv_addr.sin_family = AF_INET; -> srv_adrr.sun_family = AF_UNIX;

srv_addr.sin_port = htons(8888); -> strcpy(srv_addr.sun_path, "srv.socket")

srv_addr.sin_addr.s_addr = hton(INADDR_ANY);

bind(fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); -> bind(fd, (struct sockaddr *)&srv_addr, offsetof(struct sockaddr_un, sun_path) + strlen("srv.socket"));

sockaddr_un

sockaddr 数据结构

bind 函数创建出来的这个文件,是一个伪文件

3.3 代码

服务端

#include <stdio.h>  
#include <unistd.h>  
#include <sys/socket.h>  
#include <strings.h>  
#include <string.h>  
#include <ctype.h>  
#include <arpa/inet.h>  
#include <sys/un.h>  
#include <stddef.h>  
 
#include "wrap.h"  
 
#define SERV_ADDR  "serv.socket"  
  
int main(void)  
{  
    int lfd, cfd, len, size, i;  
    struct sockaddr_un servaddr, cliaddr;  
    char buf[4096];  

    lfd = Socket(AF_UNIX, SOCK_STREAM, 0);  
  
    bzero(&servaddr, sizeof(servaddr));  
    servaddr.sun_family = AF_UNIX;  
    strcpy(servaddr.sun_path, SERV_ADDR);  

    len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);     /* servaddr total len */  
  
    unlink(SERV_ADDR);                              /* 确保bind之前serv.sock文件不存在,bind会创建该文件 */  
    Bind(lfd, (struct sockaddr *)&servaddr, len);           /* 参3不能是sizeof(servaddr) */  

    Listen(lfd, 20);  

    printf("Accept ...\n");  
    while (1) {  
        len = sizeof(cliaddr);  //AF_UNIX大小+108B  
  
        cfd = Accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t *)&len);  
  
        len -= offsetof(struct sockaddr_un, sun_path);      /* 得到文件名的长度 */  
        cliaddr.sun_path[len] = '\0';                       /* 确保打印时,没有乱码出现 */  
  
        printf("client bind filename %s\n", cliaddr.sun_path);  
 
        while ((size = read(cfd, buf, sizeof(buf))) > 0) {  
            for (i = 0; i < size; i++)  
                buf[i] = toupper(buf[i]);  
            write(cfd, buf, size);  
        }  
        close(cfd);  
    }  
    close(lfd);  
 
    return 0;  
}  

客户端

#include <stdio.h>  
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>  
#include <strings.h>  
#include <string.h>  
#include <ctype.h>  
#include <arpa/inet.h>  
#include <sys/un.h>  
#include <stddef.h>  
 
#include "wrap.h"  
 
#define SERV_ADDR "serv.socket"
#define CLIE_ADDR "clie.socket" 
  
int main(void)  
{  
    int cfd, len;  
    struct sockaddr_un servaddr, cliaddr;  
    char buf[4096];  
  
    cfd = Socket(AF_UNIX, SOCK_STREAM, 0);  
  
    bzero(&cliaddr, sizeof(cliaddr));  
    cliaddr.sun_family = AF_UNIX;  
    strcpy(cliaddr.sun_path,CLIE_ADDR);  
  
    len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path);     /* 计算客户端地址结构有效长度 */  
  
    unlink(CLIE_ADDR);  
    Bind(cfd, (struct sockaddr *)&cliaddr, len);                                 /* 客户端也需要bind, 不能依赖自动绑定*/  
      
    bzero(&servaddr, sizeof(servaddr));                                          /* 构造server 地址 */  
    servaddr.sun_family = AF_UNIX;  
    strcpy(servaddr.sun_path, SERV_ADDR);  
  
    len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);   /* 计算服务器端地址结构有效长度 */  
  
    Connect(cfd, (struct sockaddr *)&servaddr, len);  
  
    while (fgets(buf, sizeof(buf), stdin) != NULL) {  
        write(cfd, buf, strlen(buf));  
        len = read(cfd, buf, sizeof(buf));  
        write(STDOUT_FILENO, buf, len);  
    }
      
    close(cfd);
 
    return 0;  
}  

 

3.4 总结

对比 本地套 和 网络套

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

herb.dr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值