Linux_socket编程

  • 套接字通信
  • socket 接口
  • 守护进程

一.套接字通信

端口号:

  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;

一台主机可以根据ip地址定位另一台主机,而两台主机之间的通信本质是进程在通信。根据ip地址我们可以定位一台主机,而根据端口号(port)可以定位一台主机上的一个进程。这样世界上任何一个进程都可以通过IP地址+端口号来定位。【源ip+源port ----》 目的ip+目的port】这样的通信方式称为套接字通信。
采用端口号而不是pid来标识进程的原因是降低网络和进程管理的耦合度,同时也更好的区分了网络进程。一个端口号只能对应一个进程,但是一个进程可以对应多个端口号。
套接字通信主要编写的是应用层的程序,使用os提供的网络部分的系统调用进行发送和接收消息。

二.socket 接口

udp协议特点:

  • 无连接
  • 不可靠传输
  • 面向数据报

tcp协议特点:

  • 有连接
  • 可靠传输
  • 面向字节流
2.1 udp协议相关的接口
  1. 创建socket文件

image.png

  • 返回值:返回一个文件描述符,后续输入输出操作文件描述符
  • domain:由于套接字通信可以进行网络通信,也可以进行本地通信,因此这个参数用来确定通信域
    • AF_INET:网络通信
    • AF_UNIX:本地通信
  • type:表示通信类别是面向字节流还是面向数据报
    • SOCK_DGRAM: 面向数据报
    • SOCK_STREAM:面向字节流
  • protocol:默认0即可,os会自动根据type来确定协议是tcp还是udp
  1. 绑定socket

image.png

  • 返回值:bind成功返回0,失败返回-1
  • sockfd:socket文件描述符
  • addrlen:addr对象的实际大小
  • addr:将端口号和ip地址 与当前进程绑定

image.png
addr中实际传入的对象是sockadd_in或者sockaddr_un,封装为sockaddr只是为了统一参数类型。因为传入的参数类型为sockaddr,所以需要根据首部16位来确定真实的参数类型。

  • 如果传入的参数是sockaddr_in 实例化后的对象,那么表示为网络通信,需要指明ip地址和端口号
    • 由于通信双方的字节序(大端/小端)有可能不同,所以网络中规定,发送到网络的数据都以大端方式存储。转换函数库中已经提供
    • uint32_t htonl(uint32_t hostlong) 将32位主机序列转换位32位网络序列
    • uint32_t ntohl(uint32_t netlong) 将32位网络序列转换位32位主机序列
    • uint16_t ntohs(uint16_t netlong)
    • uint16_t htons(uint16_t hostlong)
    • 其中h:host主机 n:net网络 l:long s:short
  • 如果传入的参数是sockaddr_un 实例化后的对象,那么表示为本地通信

sockaddr_in 在头文件netinet/in.h 和 arpa/inet.h 中

注意:

  • 服务器绑定的ip地址最好由os决定:使用INADDR_ANY填入参数
  • 客户端的绑定由os在合适的时候完成,程序员不需要自己绑定,防止出现端口冲突问题。
  1. 接收消息

image.png

  • 返回值:成功返回读取到的字节数,失败返回-1
  • sockfd:文件描述符
  • buf:要接收数据的缓存区
  • len:要接收数据的长度
  • flags:设置为0即可
  • src_addr:输出型参数,会设置为发送方的ip地址和端口号
  • addrlen:src_addr指针指向对象的长度,一定要初始化
  1. 发送消息

image.png

  • sockfd:文件描述符
  • buf:发送数据缓存区
  • len:发送数据缓冲区的大小
  • dest_addr:填写对端的ip地址和端口号
  • addrlen:dest_addr所指向对象的大小
2.2 tcp协议相关的接口
  1. 创建socket文件-同上
  2. 绑定socket-同上
  3. 监听listen

image.png

  • 功能:将sockfd文件设置为监听状态,使之有能力接收来自客户端的连接
  • sockfd:要设置为监听状态的文件描述符
  • backlog:tcp协议在底层维护的全连接队列,最大长度是:backlog+1,如果全连接队列满了,其他到来的连接会在半连接队列中,这个值不能大,否则会导致客户端长时间得不到响应,降低用户体验
  • 返回值:成功返回0,失败-1
  1. accept服务器接收连接

image.png

  • 如果服务器成功接收来自客户端的连接,那么将创建一个socket文件用于和这个客户端传输数据
  • 返回值:返回一个新建的socket文件描述符
  • sockfd:传入调用socket()函数创建的监听套接字文件描述符 ,这两个文件描述符的作用不同,一个是为了接收来自客户端的连接,一个是与客户端进行传输数据
  • addr:保存的是客户端的ip地址+port
  • addrlen:addr指向对象的实际大小
  1. connet客户端发起连接

image.png

  • 返回值:成功返回0,失败返回-1
  • sockfd:客户端创建的socket文件描述符
  • addr:传入要连接的服务器的ip地址和端口号
  • addrlen:addr指向对象的大小
  1. 读数据/写数据

image.png
image.png

  • 由于tcp协议是面向字节流的,所以可以用文件的接口进行读写

三.守护进程

在linux中,使用ps axj可以查看运行中的进程信息。在命令行中输入sleep 1000 | sleep 100000 | sleep 2000 & 然后使用ps axj | head -1 && ps axj | grep sleep查看sleep进程的信息。

image.png
  • PPID:当前进程的父进程的id
  • PID:当前进程的id
  • PGID:进程组id, 一条指令对应一个进程组。比如:前面3个 sleep 在一条指令中,所以这三个进程为一个组,PGID为进程组的组长id
  • SID:会话id,在xshell中,一个输入框对应一个会话,下面为两个会话窗口
  • image.png
  • TTY:终端,一个会话对应一个终端,程序的打印结果是输出到终端上的。

会话,进程组,进程的关系:
一个会话内有许多进程组,一个进程组内有许多进程。
image.png
如果我们关闭一个会话,那么会话内部的进程将全部退出。众所周知,服务器是需要一直运行的,所以服务器不能用用户进行命令行解释的会话来运行,而需要单独成一个会话,这个会话不能轻易退出。单独成一个会话的进程叫做守护进程。
守护进程需要满足的条件:

  1. 不能是原来进程组的组长
  2. 进程路径可能会更改
  3. 文件描述符0 1 2 需要特殊处理,重定向到/dev/null 文件
  4. 需要忽略一些信号
  5. 需要调用setsid()创建会话

代码模拟:
凡是调用该函数的进程都将变为守护进程,除非调用kill,否则不会退出。

void daemon()
{
    // 特殊处理信号
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    // 保证自己不是组长
    if (fork() > 0)
        exit(0);

    // 创建会话
    pid_t sid = setsid();
    // 成功返回会话id  失败返回-1
    if (sid < 0)
    {
        exit(1);
    }

    // 调用chdir()更改路径

    // 特殊处理0 1 2
    int fd = open("/dev/null", O_RDWR);
    if (fd == -1)
    {
        exit(2);
    }
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);

    close(fd);
}
  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值