0


简明linux系统编程--互斥锁--TCP--UDP初识

1.互斥锁

互斥锁也是和信号量一样,用于进行任务之间的通信;

我们的这个互斥锁,分为上锁和解锁,我们的某一个进程占用这个资源的时候,就会把这个共享区域上锁,表示这个空间资源已经被使用,其他的想要使用这个资源的进程就会被挂起,直到我们的这个正在使用资源的进程使用完毕,其他的被挂起的进程才可以使用这个资源,这个资源就会被从原来的上锁状态到现在的解锁状态,被其他的进程使用;

实际上这个互斥锁主要是是被线程使用,互斥锁不可能会同时被两个线程拥有,我们把这个称之为排他性;

已经处于加锁的状态的互斥锁,被其他的线程访问,这个线程就会被加锁,只有这个互斥锁解锁的时候,其他的线程才可以使用;

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>

//两个线程函数的声明
void *thread1_function(void *arg);
void *thread2_function(void *arg);

pthread_mutex_t mutex;//创建互斥锁

//主函数进行互斥锁的创建和初始化,以及线程的创建
int main(void)
{
    pthread_t pthread1, pthread2;
    int ret;

    //初始化互斥锁
    //第二个就是默认的配置选项
    pthread_mutex_init(&mutex, NULL);

    ret = pthread_create(&pthread1, NULL, thread1_function, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(1);
    }

    ret = pthread_create(&pthread2, NULL, thread2_function, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(1);
    }

    //线程的连接,第一个参数就是连接线程的线程号
    pthread_join(pthread1, NULL);
    pthread_join(pthread2, NULL);
    printf("The thread is over, process is over too.\n");

    return 0;
}

//线程1的操作:上锁
void *thread1_function(void *arg)
{
    int i;

    while(1)
    {
        //这个函数就是对于这个进行初始化的互斥锁进行上锁
        pthread_mutex_lock(&mutex);
        for(i = 0; i < 10; i++)
        {
            printf("Hello world\n");
            sleep(1);
        }
        //循环休眠之后进行解锁的操作
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    return NULL;
}

//线程2的操作:先是阻塞挂起,然后加锁(线程1已经占用完成)
void *thread2_function(void *arg)
{
    int i;
    //确保线程1先运行,开始的时候让这个线程1先运行
    sleep(1);

    while(1)
    {
        //尝试加锁,但是会处于阻塞状态
        pthread_mutex_lock(&mutex);
        for(i = 0; i < 10; i++)
        {
            //这个就是线程1使用完之后,才会打印这个good morning
            printf("Good moring\n");
            sleep(1);
        }
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    return NULL;
}

2.信号

2.1介绍

数据传输方式:管道,共享内存,消息队列;

信号主要用途:传递控制命令,主要是进程和应用程序之间,或者是应用程序之间;

2.2信号的内核机制

其实这个信号我们经常使用,只不过这次是说出了他的真正的名字,我们之前使用的这个ctrl+c就是退出这个程序,ctrl+z就是强制退出,这些都是我们的信号;

我们的这个信号有的是我们自己可以控制的,有的就是属于内核层面上面的,是固定的,即使我们进行自定义,他也不会按照我们的定义去修改;

下面的这个就是我们传递命令行参数,通过这个kill函数控制第二个程序里面运行的线程,我们的这个kill函数里面的传递的这个内容就是相当于信号,控制这个function里面的线程的执行;

int main(int argc, char *argv[])
{
    kill(atoi(argv[1]), SIGQUIT);

    return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void function(int signo);

int main(void)
{
    int i = 0;

    printf("pid = %d\n", getpid());

    signal(SIGINT, function);
    signal(SIGKILL, function);

    while(1)
    {
        printf("Count to %d\n", ++i);
        sleep(1);
    }

    return 0;
}

void function(int signo)
{
    //把ctrl+c的这个处理机制重写了
    if(signo == SIGINT)
    {
        printf("You have just triggered a ctrl+c operation.\n");
        exit(1);
    }

    else if(signo == SIGQUIT)
        printf("Trig a SIGQUIT signal.\n");
}

有些时候,我们传递这个信号的时候,这个控制程序并不会按照我们的这个设计输出打印结果,例如这个,我们虽然指定了这个信号的打印结果,但是如果这个信号是内核层面的,我们就无法进行修改,这个时候打印的时候就不会按照我们的设计进行打印,而是按照系统的内核设计;

3.linux网络编程概述

3.1一览七层协议

3.2一览数据传输过程

发送就是封装的过程,接收数据就是解封的过程,类似于我们的这个快递的传输过程,发送这个快递的时候需要不断的进行打包,当我们取到这个快递的时候就需要不断地进行包装的拆解;

这个里面的主机A就相当于这个快递发送端,这个主机B就相当于这个我们对于这个数据进行拆解的过程;

3.3四层网络模型

下面的这个四层模型就是上面的这个模型简化之后得到的:

TCP协议面向连接,建立连接之后,这个内容才会被发送,稳定传输数据;

UDP不是面向连接的,不稳定,不可靠,可能发送端和接收端没有建立连接,这个数据就会被发送出去了;

TCP因为确认连接的过程繁琐,传输的效率很低,UDP的传输效率相对较高,我们需要根据我们的具体需求进行选择;

当我们发送的数据比较大的时候,就是使用的UDP协议,例如看视频,顶多出现卡顿现象;

如果我们需要确保接收端收到数据,我们需要使用更加安全的TCP;

3.4服务端和客户端的数据交互

4.TCP服务端编程

#include <stdio.h>

//下面的两个头文件是使用socket函数需要包含的
#include <sys/types.h>
#include <sys/socket.h>

//htons函数需要包含下面的这个头文件
#include <arpa/inet.h>

#include <netinet/in.h>
#include <unistd.h>

#define PORT_ID    8800
#define SIZE    100

int main(void)
{
    int sockfd, client_sockfd;
    //这个结构体的
    struct sockaddr_in my_addr, client_addr;
    int addr_len;
    char welcome[SIZE] = "Welcome to connect to the sever!";

    //1.socket()创建用于通信的节点,返回节点的描述符-----------------------------------------------------
    //第一个参数配置通信域
    //AF_INET表示我们使用的是IPV4标准
    //SOCK_STREAM表示的就是TCP协议的套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    //2.bind()---------------------------------------------------------------------------------------
    my_addr.sin_family = AF_INET;

    //设置端口号,应用层上面的,这个时候,
    //因为计算机和网络的大小端可能不统一,
    //我们使用这个htons函数把主机和网络的字节序进行转换统一(全部转换为大端模式)
    my_addr.sin_port = htons(PORT_ID);

    //INADDR_ANY参数表示的是监视计算机上面的所有网卡的数据
    my_addr.sin_addr.s_addr = INADDR_ANY;

    //把一个名字绑定到我们的套接字上面
    bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

    //3.listen():监听套接字的连接---------------------------------------------------------------------
    //sockfd表示需要监听的套接字描述符

    listen(sockfd, 10);

    addr_len = sizeof(struct sockaddr);

    while(1)
    {
        printf("Server is waiting for client to connect:\n");
        //4.accept():等待客户端的请求----------------------------------------------------------------------------------
        //第二个参数:访问过来的客户端的地址
        //这个就是输出型参数(运行完之后客户端的地址就被放到这个位置,不需要我们手动填充)
        //accept返回值就是新的套接字描述符,新的客户端描述符,客户端有请求,就会返回新的套接字描述符
        client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
        //inet_ntoa函数作用就是把我们的internet主机地址从二进制转换为点分的10进制,例如这个192.168.等等之类的
        printf("Client IP address = %s\n", inet_ntoa(client_addr.sin_addr));

        //5.send()服务器发送消息到客户端--------------------------------------------------------------------------------
        //第二个参数就是发送的内容char welcome[SIZE] = "Welcome to connect to the sever!";
        send(client_sockfd, welcome, SIZE, 0);
        printf("Disconnect the client request.\n");

        //6.close()---关闭客户端的连接---------------------------------------------------------------------------------
        close(client_sockfd);
    }

    close(sockfd);

    return 0;
}

5.TCP客户端编程

#define PORT_ID    8800
#define SIZE    100

//./client IP  : ./client 192.168.0.10
int main(int argc, char *argv[])
{
    int sockfd;
    //这个结构体里面的是服务器端的这个地址
    struct sockaddr_in server_addr;
    char buf[SIZE];

    //当我们输入的参数后面没有这个IP地址的时候,我们需要给出这个提示信息,提示用户给出来IP地址
    //agrc表示的就是参数的个数
    if(argc < 2)
    {
        printf("Usage: ./client [server IP address]\n");
        exit(1);
    }

    //1.socket()
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    //2.connect()
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT_ID);
    //argv[1]这个表示的就是命令行参数里面和IP地址相关的这个参数
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));

    //recv函数表示服务器端对于客户端发来的数据的接受,buf这个表示接受的信息内容放到buf里面去
    recv(sockfd, buf, SIZE, 0);
    printf("Client receive from server: %s\n", buf);

    close(sockfd);

    return 0;
}

6.UDP服务端编程

和上面的这个TCP想比较,这个UDP就简单了很多:

UDP没有accept函数获得客户端的信息,我们无法知道谁发送给我们的信息;

我们想要获得这个谁发送的,我们使用这个recvfrom知道谁发送到我们客户端的;

#define PORT_ID    8800
#define SIZE    100

int main(void)
{
    int sockfd;
    struct sockaddr_in my_addr, client_addr;
    int addr_len;
    char buf[SIZE];

    //1.socket()
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    //2.bind()
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(PORT_ID);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

    addr_len = sizeof(struct sockaddr);

    while(1)
    {
        printf("Server is waiting for client to connect:\n");
        //4.recvfrom()函数可以让我们知道这个接收到的数据的来源
        recvfrom(sockfd, buf, SIZE, 0, (struct sockaddr *)&client_addr, &addr_len);
        printf("Server receive from client: %s\n", buf);
    }

    close(sockfd);

    return 0;
}

7.UDP客户端编程

#define PORT_ID    8800
#define SIZE    100

//./client IP  : ./client 192.168.0.10
int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in server_addr;
    char buf[SIZE];
    int i;

    if(argc < 2)
    {
        printf("Usage: ./client [server IP address]\n");
        exit(1);
    }

    //1.socket()
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT_ID);
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);

    for(i = 0; i < 10; i++)
    {
        sprintf(buf, "%d\n", i);
        sendto(sockfd, buf, SIZE, 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
        printf("Client sends to server %s: %s\n", argv[1], buf);
        sleep(1);
    }

    close(sockfd);

    return 0;
}
标签: linux 互斥锁 解锁

本文转载自: https://blog.csdn.net/binhyun/article/details/142388347
版权归原作者 阑梦清川 所有, 如有侵权,请联系我们删除。

“简明linux系统编程--互斥锁--TCP--UDP初识”的评论:

还没有评论