linux网络编程(一)——socket通信

本文深入介绍了Linux下的socket网络编程,包括socket的定义、TCP/IP协议、网络通信原理,以及socket通信的完整流程和示例代码。通过示例展示了服务器端和客户端的创建、连接、数据收发过程,帮助读者理解socket在实际应用中的工作方式。
摘要由CSDN通过智能技术生成

本期主题:
介绍linux网络编程之socket通信



1.socket定义

1.1 从操作系统层面讲

socket是一种特殊的文件

在操作系统中,所有的IO设备(磁盘、外设、网络等)都被模型化成为文件,所有的输入输出动作都被当成相应的文件来进行读、写操作。
这些文件都通过操作系统的VFS机制(虚拟文件系统机制)挂载在linux内核中,对外提供统一的文件操作接口。
因此在网络通信中,为了适配各种复杂的网络协议栈,在网络和进程之间添加了一个抽象层,这一层就被称为socket(套接字),套接字的作用:

  • 客户端和服务器通过套接字接口来建立连接,连接以文件描述符的形式提供给进程,客户端和服务器通过读写这些描述符来实现彼此之间的通信,因此从这个角度来说,socket其实是一种特殊的文件
    在这里插入图片描述

1.2 从TCP/IP协议栈讲

socket是对ip地址、传输协议、端口号的抽象,是从应用层进入到传输层的接口

1 TCP/IP协议是什么,与ISO7层模型的对照关系

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,不同于ISO模型的7层结构,TCP/IP协议将所有的TCP/IP协议归类到4个抽象层中,分层可以按照如下方式:
应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
传输层:TCP,UDP
网络层:IP,ICMP,OSPF,EIGRP,IGMP
数据链路层:SLIP,CSLIP,PPP,MTU
在这里插入图片描述
对比7层网络协议和4层协议可知socket的具体位置:
在这里插入图片描述

2 网络中的两台主机是如何传播的

在TCP/IP协议中,两台网络中的主机通过路由器相连,各主机的应用通过刚介绍的各个层次来进行数据的传播
在这里插入图片描述
在本地的进程之间进行通信,需要知道的是两个进程的进程号PID,但是进程号只是在本地的概念,在网络通信中,进程号是很有可能发生冲突的,因此我们使用ip地址+传输层网络协议+端口号来标明想通信的进程。按照这样的思路,我们可以将网络通信简化成以下模型:
在这里插入图片描述
从这张图来看,socket是一个对上承接应用进程,对下对运输层协议的一层抽象层。因此,可将socket看成一个对 ip地址、传输协议、端口号的一个抽象内容

2.socket使用的例子

2.1 socket通信流程

socket的使用与linux中对其他设备的使用模式类似,先打开设备,然后调用对应的API,然后关闭设备。
其中TCP socket的一个常用交互模式如下:
在这里插入图片描述

2.2 示例代码

//server.c
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>

#define MY_SOCK_PATH "/somepath"
#define LISTEN_BACKLOG 50

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)    

#define SERVERADDR      "192.168.123.100"
#define SERPORT         6600
int
main(int argc, char *argv[])
{
    int       sock_fd, cfd = 0;
    int       ret, i = 0;
    socklen_t len = 0;
    char recv_buf[100] = {0};
    struct sockaddr_in seraddr, cliaddr = {0};
//stpe1: 创建socket套接字
    sock_fd = socket(AF_INET, SOCK_STREAM, 0); //用AF_INET就可,用AF_UNIX就一直bind失败
    if (sock_fd == -1)
        handle_error("socket");
    printf("socket success!\n");

    seraddr.sin_family = AF_INET;
    // seraddr.sin_addr.s_addr = inet_addr(SERVERADDR);
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    seraddr.sin_port = htons(SERPORT);
    printf("ip addr: %ld, port: %d\n", seraddr.sin_addr.s_addr, seraddr.sin_port);

//step2: 将socket以及ip port绑定在一起
    if (bind(sock_fd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
        handle_error("bind");
    printf("bind success\n");

//step3: 监听该端口
    if (listen(sock_fd, LISTEN_BACKLOG) == -1)
        handle_error("listen");
    printf("listen success\n");
//step4: 接受该端口,并且返回与其相连的socket另一端
    len = sizeof(cliaddr);
    cfd = accept(sock_fd, (struct sockaddr *)&cliaddr, &len);
    if (cfd == -1)
        handle_error("accept");
    printf("accept success!\n");
//step5: 接收另外一端发来的数据
    ret = recv(cfd, recv_buf, sizeof(recv_buf), 0);
    if (ret == -1)
        handle_error("recv");
    printf("recv success, the recv number is : \n");
    for (i = 0; i< sizeof(recv_buf); i++)
    {
        printf("recv_buf[%d] : %d \n", i, recv_buf[i]);
    }
    
}
//client.c
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>

#define MY_SOCK_PATH "/somepath"
#define LISTEN_BACKLOG 50

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)    

#define SERVERADDR      "192.168.123.100"
#define SERPORT         6600
int
main(int argc, char *argv[])
{
    int       sock_fd, cfd = 0;
    int       ret, i = 0;
    socklen_t len = 0;
    char send_buf[100] = {0};
    struct sockaddr_in seraddr, cliaddr = {0};

    sock_fd = socket(AF_INET, SOCK_STREAM, 0); //用AF_INET就可,用AF_UNIX就一直bind失败
    if (sock_fd == -1)
        handle_error("socket");
    printf("socket success!\n");

    seraddr.sin_family = AF_INET;
    // seraddr.sin_addr.s_addr = inet_addr(SERVERADDR);
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    seraddr.sin_port = htons(SERPORT);
    printf("ip addr: %ld, port: %d\n", seraddr.sin_addr.s_addr, seraddr.sin_port);
    // if (bind(sock_fd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
    //     handle_error("bind");
    // printf("bind success\n");

    // if (listen(sock_fd, LISTEN_BACKLOG) == -1)
    //     handle_error("listen");
    // printf("listen success\n");

    // cfd = accept(sock_fd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
    // if (cfd == -1)
    //     handle_error("accept");
    // printf("accept success!\n");

    // ret = recv(cfd, recv_buf, sizeof(recv_buf), 0);
    // if (ret == -1)
    //     handle_error("recv");
    // printf("recv success, the recv number is : \n");
    // for (i = 0; i< sizeof(recv_buf); i++)
    // {
    //     printf("recv_buf[%d] : %d, ", i, recv_buf[i]);
    // }
    
    if (connect(sock_fd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
        handle_error("connect");
    printf("connect success\n");

    for (i = 0; i< sizeof(send_buf); i++)
    {
        send_buf[i] = i;
    }

    ret = send(sock_fd, send_buf, sizeof(send_buf), 0);
    if (ret == -1)
        handle_error("send");
    printf("send success\n");

}

github链接地址为:源码地址

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值