{C++系列} UNIX高级编程 day15 基于UDP网络编程 线程基础 线程同步 锁
in C/C++ with 0 comment

{C++系列} UNIX高级编程 day15 基于UDP网络编程 线程基础 线程同步 锁

in C/C++ with 0 comment

复习

一,基于TCP的网络编程
二,并发服务器的实现

#一,基于UDP的网络编程

TCP是安全的 可靠的 面向连接的 面向数据流的
UDP是高效的 不可靠的 面向数据包的 不需要连接

服务器端编程步骤

客户端编程步骤:

#include <sys/socket.h>
ssize_t     recvfrom(int socket, void *restrict buffer, size_t length, int flags,  struct sockaddr *restrict address, socklen_t *restrict address_len);

功能:从一个socket上接手消息。
参数

Sockfd:      指定了socket
Buf:    存储接收到的消息的缓存地址
Len:    指定了buf的大小
Flags:    0
Src_addr :        不是NULL,存储源地址。如果为NULL,那么addrlen也置为NULL。
Addrlen:        值-结果    参数,在调用之前初始化,调用之后返回确切地址的大小。    同accept类似

返回值
错误 -1
成功 接收到的字节数
对方正常关闭 返回0

#include <sys/socket.h>
ssize_t     sendto(int socket, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);

功能:在一个socket上发送消息
参数

Sockfd:    指定了socket
Buf:        指定了存放消息的缓存地址
Len:        指定了消息的长度
Flags:       0
Dest_addr:        指定 了目标地址
Addrlen:    指定了dest_addr   的大小

返回值
错误 -1 errno被设置
成功 返回发送出去的字节数

举例

编写代码实现基于udp的服务器端和客户端

服务器向外提供服务,将字符串全部转换为大写

服务器代码:

  
1 #include<stdio.h>
  2 #include"../net/r_net.h"
  3 #include<ctype.h>
  4 int main(){
  5         int s_fd;
  6         SA_4 serv;
  7         int rcv,cli;
  8         int cli_len = sizeof(cli);
  9         char buf[128];
 10         //创建一个socket
 11         s_fd = socket(AF_INET,SOCK_DGRAM,0);
 12         //初始化服务器的信息
 13         serv.sin_family = AF_INET;
 14         serv.sin_port = htons(7777);
 15         serv.sin_addr.s_addr=htonl(INADDR_ANY);
 16         //将s_fd和服务器ip地址和端口号绑定
 17         int b = bind(s_fd,(SA *)&serv,sizeof(SA_4));
 18         if(b == -1){
 19                 perror("bind");
 20                 return -1;
 21         }
 22         if(s_fd == -1){
 23                 perror("sokcet");
 24                 return -1;
 25         }
 26         while(1){
 27                 //阻塞等待获取客户端
 28                 rcv = recvfrom(s_fd,buf,128,0,(SA *)&cli,&cli_len);
 29                 if(rcv >0){//接收到客户端的数据
 30                         for(int i=0;i<rcv;i++){
 31                                 buf[i]=toupper(buf[i]);
 32                         }
 33                         //响应客户端的消息
 34                         sendto(s_fd,buf,rcv,0,(SA *)&cli,sizeof(SA_4));
 35                 }
36         }
 37         close(s_fd);
 38         return 0;
 39 }

客户端:

 1 #include<stdio.h>
  2 #include"../net/r_net.h"
  3 int main(int argc,char* argv[]){
  4         int fd;
  5         char* msg = "this is a test\n";
  6         SA_4 serv;
  7         char buf[128];
  8         //创建一个socket
  9         fd = socket(AF_INET,SOCK_DGRAM,0);
 10         if(fd == -1){
 11                 perror("socket");
 12                 return -1;
 13         }
 14         //初始化服务器的地址和端口
 15         serv.sin_family = AF_INET;
 16         serv.sin_port=htons(7777);
 17         //将目标ip地址的转化为binary格式
 18         inet_pton(AF_INET,argv[1],&serv.sin_addr);
 19         //向服务器发送消息
 20         int s = sendto(fd,msg,strlen(msg),0,(SA *)&serv,sizeof(SA_4));
 21         if(s == -1){
 22                 perror("sendto");
 23                 return -1;
 24         }
 25         //阻塞等待服务器的响应消息
 26         int rcv = recvfrom(fd,buf,128,0,NULL,NULL);
 27         if(rcv > 0){
 28                 write(1,buf,rcv);
 29         }
 30         close(fd);
 31         return 0;
 32 }
~

#二,线程基础

程序和进程 进程是操作系统管理资源的基本单位。 每个进程有自己的pid
线程是执行的基本单位,每个线程都有自己的tid。一个进程中有一个或者多个线程。进程中的线程共享进程的资源。
线程也有自己的私有资源。

多进程可以实现并发。

线程的目的

#三,线程的创建

在进程里创建一个新的线程 pthread_create(3)

 #include <pthread.h>
int     pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);

功能:创建一个新的线程
参数

Thread:  指定了存储线程id的地址
Attr: NULL 使用缺省的属性
Start_routine:新的线程执行的函数
Arg:是start_routine函数的唯一参数

返回值:
成功 0
错误 返回一个错误数

获取进程的pid get pif();
获取线程的tid pthread_self(3);

#include <pthread.h>
pthread_t     pthread_self(void);

功能:获取当前线程的id
参数:void
返回值:
返回当前线程的pid

举例

在进程中创建一个新的线程

gcc thread.c -lpthread

1 #include<stdio.h>
  2 #include<pthread.h>
  3 void *doit(void* arg){
  4 //线程执行的函数
  5         printf("%s\n",(char *)arg);
  6         return 0;
  7 }
  8
  9 int main(){
 10         pthread_t tid;
 11         //创建一个新的线程
 12         pthread_create(tid,NULL,doit,"new");
 13         sleep(1);
 14         //主线程调用doit函数
 15         doit("main");
 16         return 0;
 17 }

显示出当前进程pid,线程tid

1 #include<stdio.h>
  2 #include<pthread.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 void *doit(void* arg){
  6 //线程执行的函数
  7         printf("%s\t%d\t%lu\n",(char *)arg,getpid(),pthread_self());
  8         return 0;
  9 }
 10
 11 int main(){
 12         pthread_t tid;
 13         //创建一个新的线程
 14         pthread_create(tid,NULL,doit,"new");
 15         sleep(1);
 16         //主线程调用doit函数
 17         doit("main");
 18         return 0;
 19 }

#四,线程的汇合,分离,终止

1,线程的终止

Return 只是函数的返回,用在main函数中是进程的退出。
exit(3) 是终止进程,不管在什么时候使用,当前进程都会立即终止。
在线程的执行函数中,切记不要调用exit(3)

可以使用return从线程的执行函数中返回。

#include <pthread.h>
void     pthread_exit(void *value_ptr);

功能:终止当前线程
参数
retval:通过retval返回一个值。这个值可以被同一线程中调用
Pthread_jion的线程获取使用。
返回值
void

(3)使用pthread_cancel(3)终止同一进程中的另一线程。取消线程。向系统申请取消某个线程的请求

#include <pthread.h>
int     pthread_cancel(pthread_t thread);

功能:给一个线程发送取消请求
参数
Thread:指定接收取消请求的线程
返回值:
0 成功
非0 错误

2,线程的分离. Pthread_detach(3)

 #include <pthread.h>
int     pthread_detach(pthread_t thread);

功能:分离一个线程
参数
Thread:指定要分离的线程的tid
返回值
0 成功
错误 返回错误码

3,线程的汇合 pthread_join

#include <pthread.h>
int     pthread_join(pthread_t thread, void **value_ptr);

功能:汇合一个终止的线程
参数
Thread:指定了要汇合的线程的id
Retval: 如果不为空,被汇合的线程的终止状态码存储到*retval中
返回值:
0 成功
错误 返回错误码

举例

线程的终止,汇合

1 #include<stdio.h>
  2 #include<pthread.h>
  3 void *doit(void *arg){
  4         printf("%s\n",(char *)arg);
  5         return (void *)-1;
  6 }
  7 int main(){
  8         pthread_t tid;
  9         //创建一个新的线程
 10         pthread_create(&tid,NULL,doit,"new");
 11         //等待线程的汇合
 12         pthread_join(tid,NULL);
 13         doit("main");
 14         return 0;
 15 }
1 #include<stdio.h>
  2 #include<pthread.h>
  3 void *doit(void *arg){
  4         printf("%s\n",(char *)arg);
  5         return (void *)-1;
  6 }
  7 void *doit2(void *arg){
  8         printf("thread 2 is running ...\n");
  9         pthread_exit((void *)2);
 10 }
 11 int main(){
 12         void* ret;
 13         pthread_t tid;
 14         //创建一个新的线程
 15         pthread_create(&tid,NULL,doit,"new");
 16         //等待线程的汇合
 17         pthread_join(tid,&ret);
 18         printf("exit code %d\n",(int)ret);
 19         //设置新线程为分离状态
 20         //pthread_detach(tid);
 21         //创建一个新的线程
 22         pthread_create(&tid,NULL,doit2,"new");
 23         //等待线程的汇合
 24         pthread_join(tid,&ret);
 25         printf("exit code %d\n",(int)ret);
 26         doit("main");
 27         return 0;
 28 }
1 #include<stdio.h>
  2 #include<pthread.h>
  3 #include<unistd.h>
  4 void *doit(void *arg){
  5         printf("%s\n",(char *)arg);
  6         return (void *)-1;
  7 }
  8 void *doit2(void *arg){
  9         printf("thread 2 is running ...\n");
 10         pthread_exit((void *)2);
 11 }
 12
 13 void *doit3(void *arg){
 14
 15         while(1){
 16                 sleep(1);
 17                 printf("thread 3 running...\n");
 18         }
 19
 20 }
 21 int main(){
 22         void* ret;
 23         pthread_t tid;
 24         //创建一个新的线程
 25         pthread_create(&tid,NULL,doit,"new");
 26         //等待线程的汇合
 27         pthread_join(tid,&ret);
 28         printf("exit code %d\n",(int)ret);
 29         //设置新线程为分离状态
 30         //pthread_detach(tid);
 31         //创建一个新的线程
 32         pthread_create(&tid,NULL,doit2,"new");
 33         //等待线程的汇合
 34         pthread_join(tid,&ret);
 35         printf("exit code %d\n",(int)ret);
36         //创建一个新的线程
 37         pthread_create(&tid,NULL,doit3,"new");
 38         sleep(3);
 39         pthread_cancel(tid);
 40         //等待线程的汇合
 41         pthread_join(tid,&ret);
 42         if(ret == PTHREAD_CANCELED){printf("exit code %d\n",(int)ret);}
 43         doit("main");
 44         return 0;
 45 }

#五,线程同步(mutex. 条件变量 信号量)

每个线程都有自己的私有空间。私有空间即线程执行函数的栈帧。
进程的代码段,数据段,堆 所有线程共有的。

线程的执行函数最好是可重入函数

线程的执行函数访问临界资源,出现了不确定性。因为线程是异步的。

消除不确定性。让异步访问临界资源的文件变为同步访问临界资源。

举例

异步访问临界资源,造成的不确定性

  1 #include<stdio.h>
  2 #include<pthread.h>
  3 int count = 0;//临界资源
  4 void *doit(void *arg){
  5         int val;
  6         for(int i=0;i<5000;i++){
  7         val = count;
  8         val++;
  9         printf("%d\n",val);
 10         count=val;
 11         }
 12         return NULL;
 13 }
 14
 15 int main(){
 16         pthread_t tid1,tid2;
 17         //创建两个线程
 18         pthread_create(&tid1,NULL,doit,NULL);
 19         pthread_create(&tid2,NULL,doit,NULL);
 20         //阻塞等待线程的结束
 21         pthread_join(tid1,NULL);
 22         pthread_join(tid2,NULL);
 23
 24         return 0;
 25 }
~```


这些问题是由于多个线程异步访问临界资源。将异步访问变为同步访问。
`mutex`        锁
* 1,在访问临界资源之前,加锁。
* 2,加锁成功,访问临界资源。
* 3,解锁。

`Pthread_mutex_t   `        `mutex`锁类型    `mutex`是一个`互斥设备`,用于保护临界资源

Mutex 有两种类型,`unlock `       `locked`

类型的操作函数。

include <pthread.h>

int pthread_mutex_init(pthread_mutexattr_t *attr);
功能:初始化mutex锁
参数:
Mutex:指定初始化mutex锁
Mutexattr:默认初始化mutex锁
返回值:
成功 0 总是
非0 错误码

int pthread_mutex_destroy(pthread_mutexattr_t *attr);
功能:销毁mutex锁
参数:
Mutex:指定了要销毁的mutex
返回值:
成功 0 总是
非0 错误码

int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:加锁,如果处于unlocked状态,加锁立即返回。如果处于locked状态,等待其它线程unlock锁。
参数:
mutex:指定锁
返回值:
成功 0 总是
非0 错误码

int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:加锁,在锁被其它线程占用的时候,立即返回错误。
参数:
Mutex;指定了锁
返回值:
成功 0 总是
非0 错误码

int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:解锁
参数:
mutex:指定要解开的锁。
返回值:
成功 0 总是
非0 错误码


## 举例

在线程种应用锁

1 #include<stdio.h>
2 #include<pthread.h>
3 #include<sys/types.h>
4 int count = 0;
5 //静态初始化一个mutex锁
6 pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
7 void doit(void arg){
8 int val;
9 for(int i=0;i<5000;i++){
10 printf("threadid:%lu=",pthread_self());
11 pthread_mutex_lock(&mutex);
12 val = count;
13 val++;
14 printf("%dn",val);
15 count=val;
16 pthread_mutex_unlock(&mutex);
17 }
18 return NULL;
19 }
20
21 int main(){
22 pthread_t tid1,tid2;
23 //创建两个线程
24 pthread_create(&tid1,NULL,doit,NULL);
25 pthread_create(&tid2,NULL,doit,NULL);
26 //阻塞等待线程的结束
27 pthread_join(tid1,NULL);
28 pthread_join(tid2,NULL);
29 //销毁锁
30 pthread_mutex_destroy(&mutex);
31 return 0;
32 }

## 复习
* 一,基于UDP网络编程
* 二,线程基础
* 三,线程的终止,分离,汇合
* 四,线程创建
* 五,线程同步        mutex锁




一,条件变量            生产者消费者
二,信号量                环状队列
三,进程间同步        system V IPC        信号量集

    
Web服务器的文档    ppt




Responses