UNIX_C 高级编程<七>

本文详细对比了TCP和UDP两种网络通信协议的特点,并介绍了线程的基本概念、操作及线程间的同步机制,包括互斥量和信号量的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tcp协议和udp协议的比较

     

           tcp协议 —— 传输控制协议,面向链接的协议,类似打电话

                    —— 建立链接=> 进行通信 => 断开链接

                    —— 在通信的整个过程中全程保持连接

                    —— 保证了数据传递的可靠信和有序性(重点)

                    —— 提供了流量控制,也就是数据接收方会实时通知数据的发送缓冲区的大小

                   —— 是一种全双工的字节流通信方式

                    —— 服务器压力比较大,发送数据的效率比较低

          

           udp协议 —— 用户数据报协议,非面向链接的协议,类似写信

                    —— 不需要全程保持连接

                    —— 不保证数据传递的可靠性和有序性

                    —— 不提供流量控制

                    —— 是全双工的数据报通信方式

                    —— 服务器压力比较小,发送数据的效率比较高

     

基于UDP的协议通信模型

 

      服务器端:

           1、创建socket,使用socket函数

           2、准备通信地址,使用结构体类型;

           3、绑定socket和通信地址,使用bind函数

           4、进行通信,使用send/sendto/recv/recvfrom函数

           5、关闭socket,使用close函数

 

      客户端:

           1、创建socket,使用socket函数

           2、准备通信地址,使用服务器地址

           3、进行通信,使用send/sendto/recv/recvfrom函数

           4、关闭socket,使用close函数

 

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

      功能:该函数的功能与send函数类型,只不过是通过后两个参数来指定收件人的信息

          

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

      功能:该函数的功能与recv函数功能相似,只不过是通过后两个参数可以得到客户端/数据发送方的通信地址,相当于来电显示的效果

 1 //使用UDP网络模型实现通信

	 3 #include <stdio.h>
	 4 #include <stdlib.h>
	 5 #include <unistd.h>
	 6 #include <sys/types.h>
	 7 #include <netinet/in.h>
	 8 #include <arpa/inet.h>
	 9 #include <sys/socket.h>
	10 
	11 int main(void)
	12 {
	13     //1.创建socket,使用socket函数
	14     int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	15     if( -1 == sockfd )
	16     {
	17         perror("SOCKET");
	18         exit(-1);
	19     }
	20 
	21     printf("socket 创建成功!\n");
	22 
	23     //2.准备通信地址,使用结构体类型
	24 
	25     struct sockaddr_in addr;
	26     addr.sin_family = AF_INET;
	27     addr.sin_port = htons(8888);
	28     addr.sin_addr.s_addr = inet_addr("172.30.4.127");
	29 
	30     //3.绑定socket通信地址,使用bind函数
	31 
	32     int res = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
	33     if(-1== res)
	34     {
	35         perror("bind");
	36         exit(-1);
	37     }
	38 
	39     printf("绑定成功!\n");
	40 
	41     //4.进行通信,recv/recvfrom/send/sendto
	42 
	43     struct sockaddr_in recv_addr;
	44     socklen_t len = sizeof(recv_addr);
	45 
	46     char buf[100] ={0};
	47     res = recvfrom(sockfd, buf, sizeof(buf), 0 , (struct sockaddr*)&recv_add    r, &len);
	48     if(-1 == res)
	49     {
	50         perror("recv");
	51         exit(-1);
	52     }
	53 
	54     char * ip = inet_ntoa(recv_addr.sin_addr);
	55 
	56     printf("客户端%s发来的消息是:%s, 消息大小是:%d\n", ip, buf, res);
	57 
	58     //向客户端回发消息
	59     res = sendto(sockfd, "I received!", 12, 0, (struct sockaddr*)&recv_addr,    len);
	60     if(-1 == res )
	61     {
	62         perror("sendto");
	63         exit(-1);
	64     }
	65 
	66     printf("成功发送数据到客户端,发送的数据大小是:%d\n", res);
	67 
	68     //5.关闭socket,使用close函数
	69 
	70     res = close(sockfd);
	71     if( -1 == res )
	72     {
	73         perror("close");
	74         exit(-1);
	75     }
	76 
	77     return 0;
	78 }

线程的基本概念

 

      线程隶属于进程,线程是属于进程内部的程序流,目前主流的操作系统都支持多进程,而每个进程的内部有可以支持多线程

 

      进程是重量级的,每个进程都需要独立的内存空间等,因此新建进程对系统资源的消耗比较大,而线程是轻量级的,新建线程会共享所在进程的内存资源等,因此线程对系统资源的消耗比较小,当然每个线程都拥有一块独立的栈区

 

线程的基本操作

 

      线程的创建

 

1、pthread_create函数

 

#include<pthread.h>

 

intpthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);

 

           参 1:用于将新线程的编号存放在该参数指向的缓冲区中

                      pthread_t 类型 => typedef unsigned long int pthread_t;

                      不同系统中对pthtread_t的实现可能不同

 

           参 2:给NULL表示选择默认属性即可

           参 3:函数指针类型,新线程的处理函数,也就是新线程启动之后需要去执行的代码;

           参 4:用于作为第三个参数所指向函数的实参

           返回:成功返回0,失败返回错误编号

           功能:主要用于在当前正在调用的进程中创建一个新进程

 

 

           30    if( 0 != errno )

           31     {  

           32        printf("pthread_careate:%s\n", strerror(errno));

           33         exit(-1);

           34     }

 

      编译时需要加上-pthread 链接动态库 ( gcc xxx.c -pthread )

 

线程之间的关系

      执行main函数的线程叫做主线程;

      使用pthread_create函数创建出来的线程叫做子线程

      主线程和子线程之间相互独立,又相互影响,当主线程结束时,会导致进程结束,而进程结束又会导致进程中的所有线程结束

     

      pthread_t pthread_self(void);

           功能:主要用于获取当前正在调用线程的编号并返回,没有出错情况

 

     int pthread_equal(pthread_t t1, pthread_tt2);

           功能:主要用于判断参数指定的两个线程编号是否相等,如果相等返回非0,否则返回0,没有出错情况

 

线程的汇合和分离

     

       intpthread_join(pthread_t thread, void **retval);

 

           参 1:线程的编号

           惨 2:二级指针类型的返回值

           功能:主要用于汇合一个终止的线程,也就是等待第一个参数thread所指向的线程终止,如果该线

                      +程已经终止,则pthread_join函数立即返回,当然前提条件是:目标线程是可以等待的

                      +当第二个参数retval不为空时,则该函数会将所等待的目标线程的退出状态信息拷贝到

                      +*retval所指向的位置 

 

                 18   return (void*)&sum;

 

                 32          int * pc = NULL;

                 33    

                 34     errno = pthread_join(thread, (void **)&pc);

 

       intpthread_detach(pthread_t thread);

 

功能:主要用于将参数指定的线程设置为分离状态,当一个分离状态的线程终止时,会自动释放资源给系统,不需要其它线程的帮助,其他线程也不能用pthread_join函数等待

 

线程的终止

 

       void pthread_exit(void *retval);

     

           功能:主要用于终止当前正在调用的线程,通过参数返回当前线程的推出状态信息,在同一个进程中的其他线程可以通过pthread_join函数来获取该退出状态信息

 

线程的取消

     

      int pthread_cancel(pthread_t thread);

 

           功能:主要用于给参数指定的线程发送取消的请求,对于一个新创建的线程老说,默认是可以取消的

 

       intpthread_setcancelstate(int state, int *oldstate);

     

           参 1:用于设置新的状态

                 PTHREAD_CANCEL_ENABLE —— 可以被取消

              PTHREAD_CANCEL_DISABLE —— 不可以被取消

           参 2:用于带出旧的状态,给NULL表示不带出函数功能

           功能:主要用于设置当前线程是否允许被取消    

     

线程的同步(重点)

 

      基本概念

     

           当多个线程在同一时刻同时访问同一种共享资源时,可能会造成数据的不一致和混乱等问题,此时

           +就需要对多个线程进行协调,而线程之间的协调和通信就叫做线程的同步问题        

                                                                             

      使用互斥量实现线程的同步

 

           定义互斥量                        —— pthread_mutex_t mutex;

         初始化互斥量                ——pthread_mutex_init( &mutex, NULL );

         使用互斥量行加锁              —— pthread_mutex_lock(&mutex);

         访问共享资源

         使用互斥量进行解锁            —— pthread_mutex_umlock(&mutex);

         如果不再使用,则销毁互斥量         —— pthread_mutex_destroy(&mutex);

 

 

      使用信号量实现线程的同步

 

           信号量 —— 本质就是一种计数器,用于控制同时访问同一种共享资源的进程/线程个数;

          

           当信号量的初始值为1时,效果等同互斥量

 

           #include <semaphore.h>

 

           定义信号量                            —— sem_t sem;

         初始化信号量                     —— sem_init(&sum, 0, 初始值)

         获取信号量,也就是信号量减1        —— sem_wait(&sem);

         访问共享资源

         释放信号量,也就是信号量加1        —— sem_post(&sem);

         如果不再使用,则销毁信号量              —— sem_destroy(&sem) 

                

 

 

=====================================================================================

 

 

终端规范模式开启于关闭,使得缓冲和编辑失效

 

     #include <termios.h>

     #include<unistd.h>

 

    int tcgetattr(int fd, structtermios *termios_p);

        

         参 1:设备终端的文件描述符:0——标准输入,1——标准输出,2——标准错误

 

         参 2:struct termios original_mode{

                 ……

                   tcflag_t c_lflag;    //本地模式标志,控制终端编辑功能

                 cc_t     c_cc[NCCS]; 

              };

 

                   c_lflag:ICANON —— 使用标准输入模式

                        eg:original_mode.c_lflag&= ~ICANON  ——  去除标准输入模式

                            ECHO   —— 回显功能

                        eg:original_mode.c_lflag&= ~ECHO   —— 关闭回显功能

 

                   c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等

                         NCCS: VMIN —— 非规范模式读取时的最小字符数

                            eg:original_mode.c_c[VMIN] =1

    

         功能:用于获取与终端相关的参数,返回的结果保存在termios结构体中

 

    int tcsetattr(int fd, int optional_actions, const struct termios*termios_p);

        

         参 1:fd为打开的终端文件描述符,一般给 0;

         参 2:optional_actions用于控制修改起作用的时间

                * TCSANOW:     不等数据传输完毕就立即改变属性

         参 3:而结构体termios_p中保存了要修改的参数

         返回:函数在成功的时候返回0,失败的时候返回-1

         功能:用于设置终端参数参数参数


5 int _getchar(char * c_num)
	 6 {
	 7     //设置两个结构体用以存储终端相关信息
	 8     struct termios oldmode;
	 9     struct termios newmode;
	10 
	11     //获取与终端相关的参数,将结果返回给结构体
	12     if ( !0 == tcgetattr(0,&oldmode) )
	13     {
	14         perror("TCGETATTR ERROR");
	15         return -1;
	16     }
	17 
	18     //将原来的参数copy给新的结构体用以修改不影响原系统设置  
	19     newmode = oldmode;
	20 
	21     //ICANON摆在哦混输入模式,~ICANON去除该模式
	22     newmode.c_lflag &= ~ICANON;
	23     //当输入错误时不希望错误选项回显则
 	23	    newmode.c_lflag &= ~ECHO;
    23
	24     //AMIN非规范模式设置1,告诉驱动程序一次只读一个字符
	25     newmode.c_cc[VMIN] = 1;
	26 
	27     //在获取输入字符时,使终端模式处于非规模模式下,关闭缓冲与编辑
	28     if( -1 == tcsetattr(0, TCSANOW, &newmode) )
	29     {   
	30         perror("TCSETATTR ERROR");
	31         return -1;
	32     }
	33 
	34     *c_num = getchar();
	35 
	36     //获取成功后还原终端规范模式下
	37     if ( -1 == tcsetattr(0, TCSANOW, &oldmode) )
	38     {
	39         perror("TCGETATTR ERROR");
	40         return -1;
	41     }
	42 
	43     return 0;
	44 }
	45




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值