【Linux C/C++】 第08讲 多线程TCP传输文件/select模型
2017-07-23 09:47
239 查看
一、多线程
pthread.h
libpthread.so -lpthread
1.创建多线程
1.1 代码
回调函数
1.2 线程ID
pthread_t
1.3 运行线程
pthread_create
int pthread_create(pthread_t* restrict th,
const pthread_attr_t * attr,//线程属性 ,为NULL/0,使用进程的默认属性
void* (*run) (void*), //线程回调函数
void* data //传递给线程回调函数的数据
);
restrict -> 只修饰参数指针,第一次加载之后一直使用寄存器的值
结论:
<1> 进程结束,所有子线程结束
int pthread_join(pthread_t thread,
void** re//子线程结束的返回值);
等待子线程结束
<2> 创建子线程后,主线程继续完成系统分配的时间片
<3> 子线程结束就是线程函数返回
<4> 子线程和主线程有同等优先级
2.线程的基本控制
线程的状态:
ready -> runny -> deady
|
sleep/pause
结束一个线程
内部结束:
pthread_exit(void* value_ptr)
return 只能在线程函数中使用
外部结束:
pthread_cancel(pthread_t);
线程开始
3.多线程的问题
数据脏
4.多线程问题的解决
互斥锁/互斥量 mutex
定义互斥量 pthread_mutex_t
初始化互斥量 1 pthread_mutex_init
互斥量操作
置0 pthread_mutex_lock
判定互斥量 如果是0 -> 阻塞
如果是1 -> 置0,返回
置1 pthread_mutex_unlock
置1,返回
释放互斥量 pthread_mutex_destroy
结论:
互斥量只能保证锁定的代码一个线程执行,
但不能保证必须执行完
在lock与unlock之间,调用pthread_exit
或者在线程外部调用pthread_cancel 其他线程被永久死锁
pthread_cleanup_push {
pthread_cleanup_pop }
这对函数作用类似于atexit
注意:
这不是函数,而是宏
必须成对使用
二、网络编程
1.基础(ip)
1.1 网络工具
ping
ping ip地址
ping -b ip广播地址
ifconfig -a
route
lsof
netstat
1.2 网络的基本概念
网络编程采用socket模型,网络通信的本质也是进程之间的IPC(进程间通信),是不同主机之间。
识别主机:4字节整数 -> IP地址
识别进程:2字节整数 -> 应用端口
ip地址的表示:
字符串表示“192.168.0.13”
整数表示 in_addr_t
字结构表示 struct in_addr
1.3 IP地址的转换
inet_addr -> 把字符串转换为整数 (网络字节序)
inet_aton -> 把字符串转换为struct in_addr (网络字节序)
inet_network -> 把字符串转换为整数(本地字节序)
inet_ntoa -> 把结构体转换为字符串
1.4 计算机系统中的网络配置
/etc/hosts文件 -> 配置IP,域名,主机名
gethostbyname
gethostbyaddr
/etc/protocols文件 -> 配置系统支持的协议
/etc/services文件 -> 配置服务
2.TCP/UDP编程
对等模型 :AF_INET SOCK_DGRAM 默认UDP
socket
绑定IP地址bind
read/recv
recvfrom 可以获取发送者的地址
关闭close
C/S模型: AF_INET SOCK_STREAM 默认TCP
3.TCP的服务器编程模型
TCP的服务端维护多个客户的网络文件描述符
1. 多进程
2. IO的异步模型(select模型 poll模型)
3. 多线程
4.多进程池
5.多线程池
4.综合应用 -- 多进程应用
1.怎样使用多进程
2.多进程的缺陷,以及怎么解决
三、多线程TCP传输文件实例
第04讲 多进程TCP传输文件 的客户端部分不够完善,
没有处理非阻塞模式下的连接问题
1.补充一下select模型的概念
select在socket编程中还是比较重要的
可是对于初学socket的人来说都不太爱用select写程序
他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序
所谓阻塞方式block,顾名思义,就是进程或线程执行到这些函数时必须等待
某个事件发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回
可是使用select就可以完成非阻塞,所谓非阻塞方式non-block,
就是进程或线程执行此函数时不必非要等待事件发生,一旦执行立即返回
以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,
若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行
所以效率较高,它能够监视需要监视的文件描述符的变化情况-读写或异常
select函数格式:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timsval *timeout);
先说明两个结构体:
1> struct fd_set 可以理解为一个集合,这个集合中存放的是文件描述符
fd_set集合可以通过一些宏由人为来操作,比如:
FD_ZERO(fd_set*); 清空集合
FD_SET(int,fd_set*); 将一个文件描述符加入集合中
FD_CLR(int,fd_set*); 将一个文件描述符从集合中删除
FD_ISSET(int,fd_set*)检查集合中指定的文件描述符是否可以读写
2> struct timeval是一个常用的结构体,用来代表时间值,有两个成员
一个是秒数,另一个是毫秒。
select的参数具体解释:
int maxfdp: 是一个整数值,是指集合中所有文件描述符的范围,
即所有文件描述符的最大值加1
fd_set *readfds: 需要监视这些文件描述符的读变化,
根据timeout来判断是否超时,有可读的返回大于0
没有可读的返回0,错误异常返回负数
fd_set *writefds: 需要监视这些文件描述符的写变化
fd_set *errorfds: 需要监视这些文件描述符的错误异常
struct timeval* timeout: 是select的超时时间,可以使select处于三种状态
1> 若将NULL以形参传入,select置于阻塞状态
2> 若将时间设为0秒0毫秒,就变成一个纯粹的非阻塞函数,
不管文件描述符是否有变化,都立刻返回
3> timeout的值大于0,select在timeout时间内阻塞,超时之内有事件返回
超时之后立刻返回
2.提供一个完善的Linux/Unix/iOS客户端
int sock =
socket(AF_INET,SOCK_STREAM,0);
if(-1 ==
sock)
{
return
false;
}
struct
sockaddr_in serv_addr;
memset(&serv_addr,
0, sizeof(serv_addr));
serv_addr.sin_family =
AF_INET;
serv_addr.sin_addr.s_addr =
inet_addr("192.168.0.103");
serv_addr.sin_port =
htons(8888);
int r = connect(sock, (struct
sockaddr*)&serv_addr,
sizeof(serv_addr));
if (-1 == r)
{
connected = false;
printf("network connect fail\n");
return false;
}
//设置为非阻塞模式
int flags = fcntl(sock,
F_GETFL, 0);
fcntl(sock,
F_SETFL, flags |
O_NONBLOCK);
struct
fd_set fds,fdsErr;
struct
timeval timeout={0,0};
//select等待3秒,3秒轮询,要非阻塞就置0
int maxfdp = 1;
/* 假定已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开
sock=socket(...);
bind(...);
fp=fopen(...); */
while(1)
{
FD_ZERO(&fds);
//每次循环都要清空集合,否则不能检测描述符变化
FD_ZERO(&fdsErr);
FD_SET(sock,&fds);
//添加描述符
FD_SET(sock,&fdsErr);
// FD_SET(fp,&fds); //同上
// maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1
maxfdp =
sock+1;
switch(select(maxfdp,&fds,NULL,&fdsErr,&timeout))
//select使用
{
case -1:
exit(-1);break;
//select错误,退出程序
case 0:break;
//再次轮询
default:
if(FD_ISSET(sock,&fds))
//测试sock是否可读,即是否网络上有数据
{
//read fd
char buffer[1024];
ssize_t len = read(sock,buffer,1024);
if(len > 0)
{
//
}
}
else if(FD_ISSET(sock,&fdsErr))
{
//close fd
close(sock);
connected = false;
}
// end if break;
}// end switch
}//end while
服务端的文件传输部分可以直接用,把多进程改成多线程就行
socket的读写和客户端一样,可以采用select/epoll 实现
需要把收到read/recv的数据包存入一个全局的队列中
然后用多线程处理这些数据包
3. 多线程服务端
static void
create_thread(pthread_t *thread,
void *(*start_routine) (void *),
void *arg) {
if (pthread_create(thread,NULL, start_routine, arg)) {
fprintf(stderr,
"Create thread failed");
exit(1);
}
}
static void *
thread_worker(void *p) {
struct message_queue * q =
global_mq;
while (!quit) {
//取出
struct message* q = q->pop();
if (q == NULL) {
//file send
...
}
}
return
NULL;
}
int main(int argc,
char * argv[]) {
create_thread(&pid, thread_worker, 0);
}
pthread.h
libpthread.so -lpthread
1.创建多线程
1.1 代码
回调函数
1.2 线程ID
pthread_t
1.3 运行线程
pthread_create
int pthread_create(pthread_t* restrict th,
const pthread_attr_t * attr,//线程属性 ,为NULL/0,使用进程的默认属性
void* (*run) (void*), //线程回调函数
void* data //传递给线程回调函数的数据
);
restrict -> 只修饰参数指针,第一次加载之后一直使用寄存器的值
结论:
<1> 进程结束,所有子线程结束
int pthread_join(pthread_t thread,
void** re//子线程结束的返回值);
等待子线程结束
<2> 创建子线程后,主线程继续完成系统分配的时间片
<3> 子线程结束就是线程函数返回
<4> 子线程和主线程有同等优先级
2.线程的基本控制
线程的状态:
ready -> runny -> deady
|
sleep/pause
结束一个线程
内部结束:
pthread_exit(void* value_ptr)
return 只能在线程函数中使用
外部结束:
pthread_cancel(pthread_t);
线程开始
3.多线程的问题
数据脏
4.多线程问题的解决
互斥锁/互斥量 mutex
定义互斥量 pthread_mutex_t
初始化互斥量 1 pthread_mutex_init
互斥量操作
置0 pthread_mutex_lock
判定互斥量 如果是0 -> 阻塞
如果是1 -> 置0,返回
置1 pthread_mutex_unlock
置1,返回
释放互斥量 pthread_mutex_destroy
结论:
互斥量只能保证锁定的代码一个线程执行,
但不能保证必须执行完
在lock与unlock之间,调用pthread_exit
或者在线程外部调用pthread_cancel 其他线程被永久死锁
pthread_cleanup_push {
pthread_cleanup_pop }
这对函数作用类似于atexit
注意:
这不是函数,而是宏
必须成对使用
二、网络编程
1.基础(ip)
1.1 网络工具
ping
ping ip地址
ping -b ip广播地址
ifconfig -a
route
lsof
netstat
1.2 网络的基本概念
网络编程采用socket模型,网络通信的本质也是进程之间的IPC(进程间通信),是不同主机之间。
识别主机:4字节整数 -> IP地址
识别进程:2字节整数 -> 应用端口
ip地址的表示:
字符串表示“192.168.0.13”
整数表示 in_addr_t
字结构表示 struct in_addr
1.3 IP地址的转换
inet_addr -> 把字符串转换为整数 (网络字节序)
inet_aton -> 把字符串转换为struct in_addr (网络字节序)
inet_network -> 把字符串转换为整数(本地字节序)
inet_ntoa -> 把结构体转换为字符串
1.4 计算机系统中的网络配置
/etc/hosts文件 -> 配置IP,域名,主机名
gethostbyname
gethostbyaddr
/etc/protocols文件 -> 配置系统支持的协议
/etc/services文件 -> 配置服务
2.TCP/UDP编程
对等模型 :AF_INET SOCK_DGRAM 默认UDP
socket
绑定IP地址bind
read/recv
recvfrom 可以获取发送者的地址
关闭close
C/S模型: AF_INET SOCK_STREAM 默认TCP
3.TCP的服务器编程模型
TCP的服务端维护多个客户的网络文件描述符
1. 多进程
2. IO的异步模型(select模型 poll模型)
3. 多线程
4.多进程池
5.多线程池
4.综合应用 -- 多进程应用
1.怎样使用多进程
2.多进程的缺陷,以及怎么解决
三、多线程TCP传输文件实例
第04讲 多进程TCP传输文件 的客户端部分不够完善,
没有处理非阻塞模式下的连接问题
1.补充一下select模型的概念
select在socket编程中还是比较重要的
可是对于初学socket的人来说都不太爱用select写程序
他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序
所谓阻塞方式block,顾名思义,就是进程或线程执行到这些函数时必须等待
某个事件发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回
可是使用select就可以完成非阻塞,所谓非阻塞方式non-block,
就是进程或线程执行此函数时不必非要等待事件发生,一旦执行立即返回
以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,
若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行
所以效率较高,它能够监视需要监视的文件描述符的变化情况-读写或异常
select函数格式:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timsval *timeout);
先说明两个结构体:
1> struct fd_set 可以理解为一个集合,这个集合中存放的是文件描述符
fd_set集合可以通过一些宏由人为来操作,比如:
FD_ZERO(fd_set*); 清空集合
FD_SET(int,fd_set*); 将一个文件描述符加入集合中
FD_CLR(int,fd_set*); 将一个文件描述符从集合中删除
FD_ISSET(int,fd_set*)检查集合中指定的文件描述符是否可以读写
2> struct timeval是一个常用的结构体,用来代表时间值,有两个成员
一个是秒数,另一个是毫秒。
select的参数具体解释:
int maxfdp: 是一个整数值,是指集合中所有文件描述符的范围,
即所有文件描述符的最大值加1
fd_set *readfds: 需要监视这些文件描述符的读变化,
根据timeout来判断是否超时,有可读的返回大于0
没有可读的返回0,错误异常返回负数
fd_set *writefds: 需要监视这些文件描述符的写变化
fd_set *errorfds: 需要监视这些文件描述符的错误异常
struct timeval* timeout: 是select的超时时间,可以使select处于三种状态
1> 若将NULL以形参传入,select置于阻塞状态
2> 若将时间设为0秒0毫秒,就变成一个纯粹的非阻塞函数,
不管文件描述符是否有变化,都立刻返回
3> timeout的值大于0,select在timeout时间内阻塞,超时之内有事件返回
超时之后立刻返回
2.提供一个完善的Linux/Unix/iOS客户端
int sock =
socket(AF_INET,SOCK_STREAM,0);
if(-1 ==
sock)
{
return
false;
}
struct
sockaddr_in serv_addr;
memset(&serv_addr,
0, sizeof(serv_addr));
serv_addr.sin_family =
AF_INET;
serv_addr.sin_addr.s_addr =
inet_addr("192.168.0.103");
serv_addr.sin_port =
htons(8888);
int r = connect(sock, (struct
sockaddr*)&serv_addr,
sizeof(serv_addr));
if (-1 == r)
{
connected = false;
printf("network connect fail\n");
return false;
}
//设置为非阻塞模式
int flags = fcntl(sock,
F_GETFL, 0);
fcntl(sock,
F_SETFL, flags |
O_NONBLOCK);
struct
fd_set fds,fdsErr;
struct
timeval timeout={0,0};
//select等待3秒,3秒轮询,要非阻塞就置0
int maxfdp = 1;
/* 假定已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开
sock=socket(...);
bind(...);
fp=fopen(...); */
while(1)
{
FD_ZERO(&fds);
//每次循环都要清空集合,否则不能检测描述符变化
FD_ZERO(&fdsErr);
FD_SET(sock,&fds);
//添加描述符
FD_SET(sock,&fdsErr);
// FD_SET(fp,&fds); //同上
// maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1
maxfdp =
sock+1;
switch(select(maxfdp,&fds,NULL,&fdsErr,&timeout))
//select使用
{
case -1:
exit(-1);break;
//select错误,退出程序
case 0:break;
//再次轮询
default:
if(FD_ISSET(sock,&fds))
//测试sock是否可读,即是否网络上有数据
{
//read fd
char buffer[1024];
ssize_t len = read(sock,buffer,1024);
if(len > 0)
{
//
}
}
else if(FD_ISSET(sock,&fdsErr))
{
//close fd
close(sock);
connected = false;
}
// end if break;
}// end switch
}//end while
服务端的文件传输部分可以直接用,把多进程改成多线程就行
socket的读写和客户端一样,可以采用select/epoll 实现
需要把收到read/recv的数据包存入一个全局的队列中
然后用多线程处理这些数据包
3. 多线程服务端
static void
create_thread(pthread_t *thread,
void *(*start_routine) (void *),
void *arg) {
if (pthread_create(thread,NULL, start_routine, arg)) {
fprintf(stderr,
"Create thread failed");
exit(1);
}
}
static void *
thread_worker(void *p) {
struct message_queue * q =
global_mq;
while (!quit) {
//取出
struct message* q = q->pop();
if (q == NULL) {
//file send
...
}
}
return
NULL;
}
int main(int argc,
char * argv[]) {
create_thread(&pid, thread_worker, 0);
}
相关文章推荐
- Linux C TCPSocket 传输文件简单实例-多线程实现
- 【Linux C/C++】 第04讲 多进程TCP传输文件
- Linux 系统应用编程——网络编程(利用TCP/IP 模型分析数据传输过程)
- 基于select I/O模型的远程目录浏览与多线程文件下
- TCP数据传输(七):多线程改进多个客户端上传文件案例
- linux c/c++网络编程----select模型
- Linux下基于TCP的文件传输
- Linux高级编程复习 第十一章 select TCP编程模型_Socket选项_OOB_HTTP协议
- Linux下基于TCP的文件传输
- Linux 系统应用编程——网络编程(利用TCP/IP 模型分析数据传输过程)
- 基于select I/O模型的远程目录浏览与多线程文件下载
- Linux C语言下TCP传文件,并显示传输进度条
- Linux TCP server系列(6)-select模式下的多线程server
- Linux 基于TCP/IP的文件传输系统
- linux C++多线程操作文件&输出加锁
- java tcp 网络通信--使用多线程传输文件
- Linux网络编程之使用TCP传输文件
- linux 实现TCP 传输文件。
- 基于select I/O模型的远程目录浏览与多线程文件下载
- 多线程实现文件拷贝(Linux下C++)