在编程的时候需要加上对应pthread开头的头文件,gcc编译的时候需要加了-lpthread选项

第三个参数是线程的入口参数,函数的参数是void*,返回值是void*,第四个参数传递给线程函数的参数

如果创建线程失败,返回值是一个错误码,错误码通过返回值返回,我们要进行错误检查就检查函数的返回值

函数简介

pthread_create是UNIX环境创建线程函数

头文件

#include<pthread.h>

函数声明

int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);

返回值

若成功则返回0,否则返回出错编号

参数

第一个参数为指向线程标识符的指针。

第二个参数用来设置线程属性。

第三个参数是线程运行函数的起始地址。

最后一个参数是运行函数的参数。

另外

在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库

pthread_join函数及Linux线程

pthread_join使一个线程等待另一个线程结束。

如果你的主线程,也就是main函数执行的那个线程,在你其他线程退出之前就已经退出,那么带来的bug则不可估量。通过pthread_join函数会让主线程阻塞,直到所有线程都已经退出。

  int pthread_join(pthread_t thread, void **value_ptr);
    thread:等待退出线程的线程号。
    value_ptr:退出线程的返回值。

代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

我们来看一个简单的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h> /*
*定义一个宏,输出错误信息并且退出
*/
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE);\
}while() void*thread_exc(void*arg){
int j = ;
for(j = ;j < ;j++){
printf("sub Thread run%d\n",j);
}
return ;
}
int main(){ //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型
pthread_t thread_id ;
int ret;
if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != )){
ERR_EXIT("线程创建失败");
} int i = ;
for(i = ;i < ;i++){
printf("main Thread run%d\n",i);
}
printf(" thread_id =%ud\n",thread_id);
if((ret = pthread_join(thread_id,NULL))!= ){
ERR_EXIT("现在加入失败");
}
return ;
}

上面代码有一个相当重要的地方,在c语言中的类型定义

pthread_create函数的第一个参数是输入一个pthread_t类型
typedef unsigned long int pthread_t;
//come from /usr/include/bits/pthreadtypes.h
用途:pthread_t用于声明线程ID。
sizeof(pthread_t) =8
pthread_t,在使用printf打印时,应转换为u类型。
 
我在写代码的时候不小心写成了int 类型,结果一直提示egmentation fault (core dumped)
这里一定要小心呀

可以调用pthread_exit来退出线程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h> /*
*定义一个宏,输出错误信息并且退出
*/
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE);\
}while() void*thread_exc(void*arg){
int j = ;
for(j = ;j < ;j++){
printf("sub Thread run%d\n",j);
if(j == ){
//线程退出的时候会将返回值传递给pthread_join的第二个参数中
pthread_exit("abc");
}
}
return ;
}
int main(){ //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型
pthread_t thread_id ;
int ret;
if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != )){
ERR_EXIT("线程创建失败");
} int i = ;
for(i = ;i < ;i++){
printf("main Thread run%d\n",i);
}
printf(" thread_id =%ud\n",thread_id);
void*value;
if((ret = pthread_join(thread_id,&value))!= ){
ERR_EXIT("现在加入失败");
}
printf("pthread_exit return msg is %s\n",((char*)value));
return ;
}

例如当线程中i=3的时候,退出线程

pthread_exit退出的时候可以携带返回值,返回值存储在
pthread_join的第二个参数中,我们来看程序运行的代码
代码名称是thread.c我们使用gcc进行编译 gcc thread.c -g -o thread -lpthread
我们看程序运行的结果

value中的值就是线程退出的时候的返回值。

如果不调用pthred_exit退出线程,当线程执行完成之后,也可以返回值,返回值也存储在value中我们来看下面的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h> /*
*定义一个宏,输出错误信息并且退出
*/
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE);\
}while() void*thread_exc(void*arg){
int j = ;
for(j = ;j < ;j++){
printf("sub Thread run%d\n",j);
}
return "edf";
}
int main(){ //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型
pthread_t thread_id ;
int ret;
if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != )){
ERR_EXIT("线程创建失败");
} int i = ;
for(i = ;i < ;i++){
printf("main Thread run%d\n",i);
}
printf(" thread_id =%ud\n",thread_id);
void*value;
if((ret = pthread_join(thread_id,&value))!= ){
ERR_EXIT("现在加入失败");
}
printf("pthread_exit return msg is %s\n",((char*)value));
return ;
}
程序运行的结果是:

僵尸线程:

如果是新创建的线程运行结束了,而主线程一直阻塞没有调用pthread_join函数,导致子线程的资源无法释放,那么子线程就会一直处于僵尸状态

我们可以设置线程的属性来必须线程处于僵尸状态,因为有的时候主线程可能不会主动去调用pthread_join函数

可以使用pthread_detach函数来避免僵尸线程,将线程的属性设置成脱离的状态,脱离的线程不会产生僵尸线程

pthread_detach():设置线程为可分离状态,pthread有两种状态joinable状态和unjoinable状态

pthread_self():获得自身的线程号

pthread_join():使一个线程等待另一个线程结束

pthread_self():获得自身的线程

pthread_create():创建一个新的线程

pthread_detach():设置线程为可分离状态,pthread有两种状态joinable状态和unjoinable状态

pthread_exit():通过自身来结束线程,并释放资源

pthread_cancel():通过其他线程调用释放资源

pthread_cancel()可以杀死其他的线程

例如主线程要取消子线程,可以调用pthread_cancel

现在我们使用多线程的方式,把linux网络编程-socket(2)对应的代码,改成服务器能够支持多个客户端连接请求相应的功能

首先我们应该把服务器端accept等待客户端请求的代码放在while循环中,让服务器一直运行来监听客户端的请求

第二:监听到客户端的请求之后,每一个客户端对应一个线程来处理客户端的请求

我们来修改下服务器端的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h> /*
*定义一个宏,输出错误信息并且退出
*/
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE);\
}while() void*thread_exc(void* arg){
pthread_detach(pthread_self()); //将线程设置成分离状态,避免僵尸线程
int clnt_sock = *((int*)arg);
//记得关闭指针
free(arg);
char revbuf[];
while(){
memset(revbuf,,sizeof(revbuf));
int len = read(clnt_sock,revbuf,sizeof(revbuf)); //len读到数据的字节长度
if(len == ){ //说明客户端终止了数据的发送
break;
}
fputs(revbuf,stdout);
//读到多少数据就给客户端返回多少字节的数据
write(clnt_sock,revbuf,len);
}
close(clnt_sock); //记得关闭线程
} int main(int argc, char *argv[])
{
int serv_sock;
int clnt_sock; struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size; serv_sock = socket(AF_INET, SOCK_STREAM, );
if (serv_sock == -)
{
ERR_EXIT("socket创建失败");
} memset(&serv_addr, , sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(); if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -){
ERR_EXIT("bind失败");
}
//SOMAXCON系统默认的最大的客户端的连接数据 , (listen(serv_sock, 5)表示最大允许5个客户端的连接
if (listen(serv_sock, SOMAXCONN) == -){
ERR_EXIT("listen失败");
} while(){ //在while循环中一直等待客户端的监听 clnt_addr_size = sizeof(clnt_addr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if (clnt_sock == -){
ERR_EXIT("accept失败");
}
//每一个客户端的请求都开启一个线程进行处理
pthread_t thread_id ;
int ret;
//将clnt_sock通过第三个参数传递到线程函数中
int * p = (int*)malloc(sizeof(int));
*p = clnt_sock;
if((ret = pthread_create(&thread_id,NULL,thread_exc, p ))!= ){
ERR_EXIT("线程创建失败");
} } close(serv_sock); return ;
} void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit();
}

客户端的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h> /*
*定义一个宏,输出错误信息并且退出
*/
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE);\
}while() int main(int argc, char *argv[])
{
int serv_sock;
struct sockaddr_in serv_addr; serv_sock = socket(AF_INET, SOCK_STREAM, );
if (serv_sock == -)
{
ERR_EXIT("socket创建失败");
} memset(&serv_addr, , sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(); if((connect(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)))<){
ERR_EXIT("客户端connect失败");
} char revbuf[];
char sendbuf[];
memset(revbuf,,sizeof(revbuf));
memset(sendbuf,,sizeof(revbuf));
while((fgets(sendbuf,sizeof(sendbuf),stdin)!= NULL)){ write(serv_sock,sendbuf,strlen(sendbuf));
read(serv_sock,revbuf,sizeof(revbuf));
fputs(revbuf,stdout);
//读到多少数据就给客户端返回多少字节的数据
memset(sendbuf,,sizeof(revbuf));
memset(revbuf,,sizeof(revbuf));
}
close(serv_sock); return ;
}
												

linux网络编程-socket(37)的更多相关文章

  1. Linux网络编程socket选项之SO_LINGER,SO_REUSEADDR

    from http://blog.csdn.net/feiyinzilgd/article/details/5894300 Linux网络编程中,socket的选项很多.其中几个比较重要的选项有:SO ...

  2. Linux网络编程-----Socket地址API

    (1) 通用socket地址 socket网络编程接口中表示socket地址的是结构体sockaddr,其定义如下: #include<bits/socket.h> struct sock ...

  3. Linux网络编程--socket

    1.socket的核心思想是,作为服务器间的进程间通信的最底层的实现,常用的大部分网络协议都是基于socket实现. 2.socket 是如何与最终的低层收发包建立联系的? 3.socket 是如何与 ...

  4. Linux网络编程socket错误分析

    socket错误码: EINTR: 阻塞的操作被取消阻塞的调用打断.如设置了发送接收超时,就会遇到这种错误. 只能针对阻塞模式的socket.读,写阻塞的socket时,-1返回,错误号为INTR.另 ...

  5. Linux入门培训教程 linux网络编程socket介绍

    一.概念介绍 网络程序分为服务端程序和客户端程序.服务端即提供服务的一方,客户端为请求服务的一方.但实际情况是有些程序的客户端.服务器端角色不是这么明显,即互为Linux培训 客户端和服务端. 我们编 ...

  6. linux网络编程-socket(2)

    当客户端调用close函数的时候,服务器的read函数读到的数据是0读到文件结束通知,表示对端关闭了tcp连接 我们现实实现下面的功能: 1.tcp客户端从标准的输入流中得到输入数据发送到服务器,服务 ...

  7. linux网络编程-socket(1)

    上面是对应的IpV4的地址结构: sin_len整个结构的大小 sin_family协议族,对应Tcp固定为AF_INET,除了tcp协议外还支持unix域协议等 sin_port socket通信的 ...

  8. linux网络编程-socket(36)

    进程是程序的一次动态执行的过程,进程是短暂的. 一个程序可以对应多个进程,可以打开多个记事本程序,存在多个进程. 线程是进程内部中的控制序列,一个进程至少有一个执行线路. 一个进程可以存在多个线程

  9. linux网络编程-(socket套接字编程UDP传输)

    今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...

随机推荐

  1. docker出现相同的image条目的删除办法

    一.问题:在测试docker安装的prometheus系统时,由于异常操作,使用docker image ls出现了两条一模一样的条目,如下: [root@ELK prometheus]# docke ...

  2. 小谢第10问:前端JS下载文件、表格

    对于小型文件及表格下载,一般采用a标签形式 <buttonb @click="downloadTemplate()">模板下载</button> downl ...

  3. Rocket - diplomacy - LazyModuleImpLike

    https://mp.weixin.qq.com/s/gDbUto1qd7uWbpnxovr5pg   介绍LazyModuleImpLike类的实现.     1. wrapper   LazyMo ...

  4. for循环的嵌套 函数方法

    1.双层for循环:外层循环控制行,内行循环控制列 for循环嵌套例子 用五角星组成一个矩形 // 99乘法表 // for(var i = 1 ;i <= 9 ; i++){ //     f ...

  5. Java实现第八届蓝桥杯国赛 数字划分

    标题:数字划分 w星球的长老交给小明一个任务: 1,2,3-16 这16个数字分为两组. 要求: 这两组数字的和相同, 并且,两组数字的平方和也相同, 并且,两组数字的立方和也相同. 请你利用计算机的 ...

  6. Java实现 蓝桥杯VIP 算法提高 淘淘的名单

    算法提高 淘淘的名单 时间限制:100ms 内存限制:8.0MB 问题描述 by ZBY- ? 淘淘拿到了一份名单,他想对上面的名字进行处理,挑出一些特殊的名字,他请你来帮忙. 淘淘关注以下名字: 如 ...

  7. Java实现 LeetCode 117 填充每个节点的下一个右侧节点指针 II(二)

    117. 填充每个节点的下一个右侧节点指针 II 给定一个二叉树 struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每 ...

  8. Java实现 LeetCode 51 N皇后

    51. N皇后 n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 上图为 8 皇后问题的一种解法. 给定一个整数 n,返回所有不同的 n 皇后问题的解决 ...

  9. Java实现第九届蓝桥杯付账问题

    付账问题 题目描述 [题目描述] 几个人一起出去吃饭是常有的事.但在结帐的时候,常常会出现一些争执. 现在有 n 个人出去吃饭,他们总共消费了 S 元.其中第 i 个人带了 ai 元.幸运的是,所有人 ...

  10. xlua中lua对象到c#对象的转型

    lua中的类型 基础类型 #define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUM ...