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*)∑
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