Skip to content

Latest commit

 

History

History
903 lines (720 loc) · 25.5 KB

网络编程复习.md

File metadata and controls

903 lines (720 loc) · 25.5 KB

线程安全

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define PORT    1234
#define BACKLOG 5
#define MAX_BUFFER_SIZE 1024

void processcli_57(int connectfd_57,struct sockaddr_in client_57);
void *fun(void *arg);
void savedata_r(char* recvbuf,int len,char* cli_data);



struct ARG{
    int connfd;
    struct sockaddr_in client;
};
struct ST_DATA{
    int index;
};

pthread_key_t key;
pthread_once_t once=PTHREAD_ONCE_INIT;
static void destructor(void *ptr)
{
    free(ptr);
}
static void createkey_once(void)
{
    pthread_key_create(&key,destructor);

}

int main(void)
{
        int  listenfd_57, connectfd_57;
        struct sockaddr_in  server_57;
        struct sockaddr_in client_57;
        socklen_t  sin_size;
        pthread_t tid;
        struct ARG *arg;


        if((listenfd_57=socket(AF_INET, SOCK_STREAM, 0))==-1)
        {
                perror("Create socket failed");
                exit(-1);
        }
       int opt =SO_REUSEADDR;

       setsockopt(listenfd_57, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        bzero(&server_57, sizeof(server_57));
        server_57.sin_family = AF_INET;
        server_57.sin_port = htons(PORT);
        server_57.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(listenfd_57, (struct sockaddr *)&server_57, sizeof(struct sockaddr))==-1)
        {
                perror("Bind error");
                exit(-1);
        }

        if (listen(listenfd_57, BACKLOG) == -1)
       {
                perror("listen error");
                exit(-1);
        }
     sin_size = sizeof(struct sockaddr_in);
     while(1)
    {
         if ((connectfd_57 = accept(listenfd_57, (struct sockaddr *)&client_57, &sin_size)) == -1)
           {
              perror("accept error");
              exit(-1);
           }

         arg=(struct ARG*)malloc(sizeof(struct ARG));
         arg->connfd=connectfd_57;
         memcpy((void *)&arg->client,&client_57,sizeof (client_57));
         if(pthread_create(&tid,NULL,fun,(void*)arg))
         {
             perror("Pthread_create() error");
             exit(1);
         }
     }
    close(listenfd_57);
}

void processcli_57(int connectfd_57,struct sockaddr_in client_57){
    char cli_data[MAX_BUFFER_SIZE];
    char buffer_57[MAX_BUFFER_SIZE],cli_name[MAX_BUFFER_SIZE],sendbuf[MAX_BUFFER_SIZE];
    int bytes_received = recv(connectfd_57, cli_name, MAX_BUFFER_SIZE, 0);
    printf("Get a new connection from %s.\n",inet_ntoa(client_57.sin_addr));
    if (bytes_received == 0) {
        close(connectfd_57);
        printf("Disconnected");
        return;
      }
      cli_name[bytes_received]='\0';
      printf("client name is %s\n",cli_name);
          while (bytes_received  = recv(connectfd_57, buffer_57, MAX_BUFFER_SIZE,0))
          {

                buffer_57[bytes_received] = '\0';
                printf("Received client (%s) message: %s", cli_name, buffer_57);
                savedata_r(buffer_57,bytes_received,cli_data);
                int i=0;
                for(i=0; i < bytes_received ; i++)
                sendbuf[i] = buffer_57[bytes_received-i-1];
                sendbuf[i] ='\0';
                send(connectfd_57, sendbuf, strlen(sendbuf), 0);
         }
          close(connectfd_57);
          printf("Client(%s) disconnected.\nUser's data:%s\n",cli_name,cli_data);
}

void *fun(void* arg)
{
    struct ARG *info;
    info=(struct ARG*)arg;
    processcli_57(info->connfd,info->client);
    free(arg);
    pthread_exit(NULL);
}

void savedata_r(char* recvbuf,int len,char* cli_data)
{
    struct ST_DATA* data;
    pthread_once(&once,createkey_once);
    if((data=(struct ST_DATA *)pthread_getspecific(key))==NULL)
    {
        data=(struct ST_DATA *)malloc(sizeof (struct ST_DATA));
        pthread_setspecific(key,data);
        data->index=0;
    }
    int i =0;
    while(i<len-1)
    {
        cli_data[data->index++]=recvbuf[i];
        i++;
    }
    cli_data[data->index]='\0';

}

客户端

/*client.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT            1234
#define MAXDATASIZE     1024
int main(int argc, char *argv[])
{
     int fd, numbytes;
     char buf_57[MAXDATASIZE];
     //struct  hostent *        he;
     struct sockaddr_in server_57,local_57;
     if (argc != 2)
     {
         printf("Usage: %s  <IP address>\n", argv[0]);
         exit(-1);
     }

 /*if ((he = gethostbyname(argv[1])) == NULL)
    {
             perror("gethostbyname error.");
              exit(1);
    }*/

     if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
          perror("Create socket failed.");
          exit(1);
     }
     bzero(&server_57, sizeof(server_57));
     server_57.sin_family = AF_INET;
     server_57.sin_port = htons(PORT);
     //server.sin_addr.s_addr = inet_addr("10.21.48.52");
     //server.sin_addr = *((struct in_addr *) he->h_addr);
     if (inet_pton(AF_INET, argv[1], &server_57.sin_addr) <= 0) {
             perror("Invalid IP address.");
             exit(1);
      }
     if (connect(fd, (struct sockaddr *)&server_57, sizeof(struct sockaddr)) == -1)
     {
        perror("connect failed.");
        exit(1);
     }
    socklen_t len = sizeof(local_57);
    if (getsockname(fd, (struct sockaddr *)&local_57, &len) == -1) {
      perror("getsockname error.");
      exit(1);
    }
    printf("Local address: %s:%d\n", inet_ntoa(local_57.sin_addr), ntohs(local_57.sin_port));

    len = sizeof(server_57);
    if (getpeername(fd, (struct sockaddr *)&server_57, &len) == -1) {
        perror("getpeername error.");
        exit(1);
    }
    printf("Server address: %s:%d\n", inet_ntoa(server_57.sin_addr), ntohs(server_57.sin_port));

    int n=1;
    while(1){
        if(n==1)
        {
            printf("please input your name:\n");
            scanf("%s",buf_57);
            int c;
            while ((c = getchar()) != '\n' && c != EOF) {}
            send(fd, buf_57, MAXDATASIZE, 0);
            n++;
        }
            printf("Enter message to send to server,enter 'quit' to exit: ");
            fgets(buf_57, MAXDATASIZE, stdin);
            if (strcmp(buf_57, "quit\n") == 0) {
                    printf("disconnected\n");
                    close(fd);
                    exit(0);
                }
            if (send(fd, buf_57, strlen(buf_57), 0) == -1) {
                perror("send error.");
                exit(1);
            }

                if ((numbytes = recv(fd, buf_57, MAXDATASIZE, 0)) == -1) {
                    perror("recv error.");
                    exit(1);
                }

                   buf_57[numbytes] = '\0';
                   printf("Server Message(reversed): %s\n", buf_57);
                   n=5;

 }
    close(fd);
}

TCP

TCP最简易部分

/*server*/
int main()
{
    int sockfd,connfd;//wenjian miaoshu fu

    struct sockaddr_in server;
    struct sockaddr_in client;
    socklen_t addrlen=sizeof(client);

    sockfd=socket(AF_INET,SOCK_STREAM,0);//error back -1

    int opt=SO_REUSEADDR;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    bzero(&server,sizeof(server));
    server.sin_port=htons(PORT);
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);

    bindsockfd,(struct sockaddr*)&server,sizeof(server));
    listen(sockfd,BACKLOG);
    connfd=accept(sockfd,(struct sockaddr *)&client,&addrlen);

    printf("you got a connection from client's ip is %s,port is %d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    //inet_ntoa 函数来将网络字节序的 IP 地址转换为点分十进制表示的字符串。对于端口号,使用 ntohs 函数来将网络字节序的端口号转换为主机字节序的短整型值。
    
    send(connfd,"welcome\n",8,0);
    close(connfd);
    close(sockfd);

}
/*client*/
int main(int argc,char *argv[])
{
    int sockfd;
    char buf[MAXDATASIZE];
    int len;
    struct sockaddr_in server;
    if(argc !=2)
    {
        printf("Usage:%s<IP address>\n",argv[0]);
        exit(-1);
    }

    sockfd=socket(AF_INET,SOCK_STREAM,0);

    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(PORT);
    server.sin_addr.s_addr=inet_addr(argv[1]);
    //inet_addr("192.168.1.1");
    //argv 是命令行参数的数组,argv[1] 是一个字符串,表示服务器的 IP 地址。因此,需要使用 inet_addr 函数

    connect(sockfd,(struct sockaddr *)&server,sizeof(server));
    len=recv(sockfd,buf,MAXDATASIZE,0);
    buf[len]='\0';
    printf("server:%s\n",buf);
    close(sockfd);
}

套接字基本信息

//套接字类型
SOCK_STREAM 流式套接字
SOCK_DGRAM  数据报套接字
SOCK_RAW    原始套接字
    
//套接字地质结构
IPv4:sockaddr_in
IPv6:sockaddr_in6
struct in_addr{
    In_addr_t s_addr;//32位IPv4地址
}
struct sockaddr_in{
    unit8_t sin_len;
    sa_family_t sin_family;
    unit16_t sin_port;
    struct in_addr sin_addr;//32位IP地址
    char sin_zero;
}

//通用套接字地址结构
struct sockaddr{
    uint8_t sa_len;
    sa_family_t sa_family;
    char sa_data[4];
}
//许多套接字函数都使用这个通用套接字地质结构,因此需要转换,例如bind
bind(sockfd.(struct sockaddr*)&server,sizeof(server));

套接字地址函数

//网络字节顺序采用大端字节
htons();
htonl();
ntohs();
ntohl();
//s是16位短整数、l是32位长整数
inet_aton();//把点分十进制的ip地址字符串转化成网络字节顺序的二进制,成功返回长整型数,失败为0
inet_addr();//与上相同
inet_ntoa();//网络字节ip地址转化成点分十进制ip地址字符串,成功时返回指针,失败null
//这三个只能用于ipv4

//字节操纵函数
void bzero(void *dest,size_t nbytes);
//对指定地址dest中指定长度nbytes设置为0

void bcopy(const void *src,void *dest,size_t nbytes);
//指定数目的字节从srccopy到dest
int bcmp(const void *src,void *dest,size_t nbytes);
//比较两个区域前面的指定字节数,相同0,不相同非0

void *memset(void *dest,int c,size_t len);
//把前len个字节设置成c的内容
void *memcop();
//类似与bcopy,但是不能处理src与dest相重叠的情况
int memcmp(cont void *ptr1,const void *ptr2,size_t nbytes);
比较两个字符串相同0否则非0

connect()

int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
//激发三次握手,成功返回0,失败-1

如果客户端再75s内没收到SYN的响应(客户端在着75s内会尝试多次),则返回ETIMEDOUT

如果对客户端SYN的响应是RST,表明服务器在指定端口没有进程等待连接,返回ECONNREFUSED

如果客户发出的SYN最终目的地不可达,返回EHOSTUNREACH或ENETUNREACH

getsockname,getpeername,getbyname

socklen_t len = sizeof(local_57);
    if (getsockname(fd, (struct sockaddr *)&local_57, &len) == -1) {
      perror("getsockname error.");
      exit(1);
    }
    printf("Local address: %s:%d\n", inet_ntoa(local_57.sin_addr), ntohs(local_57.sin_port));

    len = sizeof(server_57);
    if (getpeername(fd, (struct sockaddr *)&server_57, &len) == -1) {
        perror("getpeername error.");
        exit(1);
    }
    printf("Server address: %s:%d\n", inet_ntoa(server_57.sin_addr), ntohs(server_57.sin_port));
struct hostent *gethostbyname(const char *hostname);
//ipv4\ipv6都可使用
//域名、点分十进制都可接收,若接收点分十进制,则拷贝到结果中,返回指向hostent结构
struct hostent{
    char *h_name;
    char **h__aliases;
    int h_addrtype;
    int h_length;
    char **h_addr_list;
}

服务器三种异常情况

1.服务器主机崩溃

客户端发送一个数据后,阻塞于recv,等待接收数据。客户端会坚持重传,试图接收ACK。重传最多12次,放弃前等待9分钟。可设置recv的超时时间改变长短。

2,服务器主机崩溃后重启

如果服务器主机不主动发送消息告知自己崩溃,那么主机重启后只能回复客户端RST

3.服务器运行时主动关闭主机

由init进程给所有进程发信号SIGTERM,然后给还在运行的进程发SIGKILI。客户端发送数据,服务器可以接收,但是只会回复客户端RST

UDP

UDP最简易服务器

/*server*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT    1234
#define BACKLOG 5
#define MAX_BUFFER_SIZE 1024



int main()
{
    int sockfd;//wenjian miaoshu fu
    int num;
    struct sockaddr_in server;
    struct sockaddr_in client;
    socklen_t addrlen=sizeof(client);
    
    char buf[MAX_BUFFER_SIZE];
    sockfd=socket(AF_INET,SOCK_DGRAM,0);//error back -1

    bzero(&server,sizeof(server));
    server.sin_port=htons(PORT);
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);

    bind(sockfd,(struct sockaddr*)&server,sizeof(server));

    while(1)
    {
        num=recvfrom(sockfd,buf,MAX_BUFFER_SIZE,0,(struct sockaddr *)&client,&addrlen);
        buf[num]='\0';
        printf("you got a connection from client's ip is %s,port is %d\nclient's info: %s",inet_ntoa(client.sin_addr), ntohs(client.sin_port),buf);
        sendto(sockfd,"welcome\n",8,0,(struct sockaddr *)&client,addrlen);
    }
     close(sockfd);
}

UDP最简易客户端

/*client*/
#define PORT            1234
#define MAXDATASIZE     1024

int main(int argc,char *argv[])
{
    int sockfd,num;
    char buf[MAXDATASIZE];
    struct hostent *he;//假如要用到gethostbyname时
    struct sockaddr_in server,peer;
    socklen_t addrlen=sizeof(server);
    if(argc !=2)
    {
        printf("Usage:%s<IP address>\n",argv[0]);
        exit(-1);
    }

    sockfd=socket(AF_INET,SOCK_DGRAM,0);

    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(PORT);
    server.sin_addr.s_addr=inet_addr(argv[1]);

    sendto(sockfd,"hello\n",6,0,(struct sockaddr *)&server,sizeof(server));

    while(1)
    {
        num=recvfrom(sockfd,buf,MAXDATASIZE,0,(struct sockaddr *)&peer,&addrlen)
       if(addrlen!=sizeof(server)||memcmp((const void *)&server,(const void *)&peer,addrlen)!=0)
        {
            printf("receive info from other server\n");
            continue;
        }
        buf[num]='\0';
        printf("server info:%s",buf);
        break;
    }
    close(sockfd);
}

UDP对数据报的处理

1.数据报丢失

那么recvfrom会一直被阻塞,解决的唯一方法就是给客户端的recvfrom设置一个超时时间

2.验证收到的响应

客户端只想知道来自服务器的响应时

if(len!sizeof(server)||memcmp((const void *)&server,(const void *)&peer,len)!=0)

比较recvfrom返回的地址长度len和服务器的地址长度,然后比较recvfrom返回的服务器地址是否一致。

另一个解决办法是,由客户端在dns中查找服务器主机名验证主机。

3.服务器未运行

服务器会以端口不可达的ICMP消息回应,但是这个消息不会传给客户端,客户端会永远阻塞于recvfrom。详见p48

UDP中使用connect

详见p49

多进程

int main()
{
    pid_t pid;

    if((pid=fork())<0)
    {
        perror("fork error");
    }
    else if (pid == 0)//child
    {
        printf("child getpid()=%d\n",getpid());
    }
    else if(pid>0)
    {
        printf("parent getpid()=%d\n",getpid());
    }
}

//fork后的东西都会执行两次,因为fork后生成了两个进程,一个进程为父进程,fork返回pid;另一个进程为父进程的子进程,fork会返回0,但该子进程也是有pid的。

例如
fork();
fork();
printf("hello\n");

该段代码会生成4个hello因为第一个fork执行后后面的forkprintf会执行两次一次在父进程一次在子进程父进程中fork也开始执行再生成一个父进程一个子进程同理子进程中也如此生成我们称这四个进程为父父进程”,“父子进程”,“子父进程”,“子子进程”。

其效果如下
int main()
{
    pid_t pid;

   if(pid = fork()>0)
   {
      if(pid=fork()>0)
      {
           printf("parent parent pid=%d\n",getpid());
      }
      else
      {
          printf("parent child pid=%d\n",getpid());
      }

   }
   else
   {
       if(pid=fork()>0)
       {
           printf("child parent pid =%d\n",getpid());
       }
       else {
           printf("child child pid=%d\n",getpid());
       }

   }
}
结果parent parent pid=54649
 <RETURN> 来关闭窗口...
child parent pid =54650
parent child pid=54651
child child pid=54652

wait、waitpid

​ 在进程编程中,wait() 函数用于等待一个子进程的终止,并获取子进程的终止状态。这个函数是 POSIX 标准中定义的,它使得父进程能够暂停执行,直到一个子进程结束。

pid_t wait(int *status);

int *status`:一个指针指向一个整数变量用于存储子进程的终止状态如果子进程正常终止,`status` 中的值是 `WEXITSTATUS(status)`,其中 `status` 是子进程退出时的状态值通常是退出代码)。如果子进程是由于信号而终止的,`status` 中的值是 `WTERMSIG(status)`,其中 `status` 是导致子进程终止的信号的编号如果子进程是非正常终止,`status` 中的值是 `WUNTRACED

wait() 函数的作用包括:

  • 阻塞父进程的执行,直到一个子进程终止。
  • 获取子进程的终止状态,包括退出代码或信号编号。
  • 如果有多个子进程结束,wait() 函数只返回其中一个子进程的信息。

waitpid() 是一个在 POSIX 兼容操作系统中使用的系统调用,它用于等待一个指定的进程 ID (PID) 的子进程结束,并获取子进程的终止状态。与 wait() 函数不同,waitpid() 允许你指定要等待的子进程,以及是否等待所有子进程结束

  • pid_t waitpid(pid_t pid, int *status, int options);
    
    `waitpid()` 函数的作用包括- 等待指定的子进程结束- 获取子进程的终止状态- 可以选择是否等待所有子进程结束- 可以选择是否返回正在等待的子进程的状态

多进程服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define PORT    1234
#define BACKLOG 5
#define MAX_BUFFER_SIZE 1024

void process_cli(int connfd,struct sockaddr_in client);

int main()
{
    pid_t pid;

    int sockfd,connfd;//wenjian miaoshu fu

    struct sockaddr_in server;
    struct sockaddr_in client;
    socklen_t addrlen=sizeof(client);

    sockfd=socket(AF_INET,SOCK_STREAM,0);//error back -1

    int opt=SO_REUSEADDR;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    bzero(&server,sizeof(server));
    server.sin_port=htons(PORT);
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);

    bind(sockfd,(struct sockaddr*)&server,sizeof(server));
    listen(sockfd,BACKLOG);

    while(1)
    {
        connfd=accept(sockfd,(struct sockaddr *)&client,&addrlen);
        if((pid=fork())>0)
        {
            close(connfd);
            continue;
        }
        else if(pid=fork()==0)
        {
            close(sockfd);
            process_cli(connfd,client);
            exit(0);
        }
        else {
            printf("fork() error\n");
            exit(0);
        }
    }
}


void process_cli(int connfd,struct sockaddr_in client)
{
    int num;
    char recvbuf[MAX_BUFFER_SIZE],sendbuf[MAX_BUFFER_SIZE],cli_name[MAX_BUFFER_SIZE];
    printf("get connection form %s.\n",inet_ntoa(client.sin_addr));
    num = recv(connfd,cli_name,MAX_BUFFER_SIZE,0);
    if(num==0)
    {
        close(connfd);
        printf("%s disconnected.\n",cli_name);
        return;
    }

    cli_name[num-1]='\0';
    printf("client's name:%s\n",cli_name);
    while(num =recv(connfd,recvbuf,MAX_BUFFER_SIZE,0))
    {
        recvbuf[num]='\0';
        printf("%s : %s",cli_name,recvbuf);

        send(connfd,"successful received\n",sizeof("successful received\n"),0);

    }
    close(connfd);

}

多进程客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT            1234
#define MAXDATASIZE     1024
/*client*/
int main(int argc,char *argv[])
{
    int sockfd;
    char buf[MAXDATASIZE];
    int num;
    struct sockaddr_in server;
    if(argc !=2)
    {
        printf("Usage:%s<IP address>\n",argv[0]);
        exit(-1);
    }

    sockfd=socket(AF_INET,SOCK_STREAM,0);

    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(PORT);
    server.sin_addr.s_addr=inet_addr(argv[1]);
    //inet_addr("192.168.1.1");
    //argv 是命令行参数的数组,argv[1] 是一个字符串,表示服务器的 IP 地址。因此,需要使用 inet_addr 函数

    connect(sockfd,(struct sockaddr *)&server,sizeof(server));
    printf("connected to server\n");
    printf("input name:");
    fgets(buf,MAXDATASIZE,stdin);
    send(sockfd,buf,strlen(buf),0);

    while(1)
    {
        fgets(buf,MAXDATASIZE,stdin);
        send(sockfd,buf,strlen(buf),0);
        num=recv(sockfd,buf,MAXDATASIZE,0);
        buf[num]='\0';
        printf("from server:\n%s",buf);

    }
    close(sockfd);
}

多进程服务器问题

1.fork是昂贵的

2.fork父子进程、兄弟进程间通信需要通信IPC机制,给通信带来困难

3.多进程一定程度上仍不能有效利用系统资源

4.系统中进程个数有限制

线程基础

int pthread_create(pthread_t *tid,const pthread_attr_t * attr,void *(*func)(void *),void *arg)
    //成功0,失败-1
    //还有返回错误EAGAIN超过线程数目限制 ENOMEN没有足够内存 EINVAL无效attr值
    
//pthread_t *tid:这是一个指向 pthread_t 类型变量的指针,该变量用于存储新创建线程的标识符。pthread_t 是一个无符号整数类型,用于唯一标识线程。
    
//const pthread_attr_t *attr:这是一个指向 pthread_attr_t 类型变量的指针。pthread_attr_t 是一个结构体类型,用于指定新创建线程的属性。如果你不需要设置特定的属性,你可以传递一个指向默认属性的指针,即 NULL。
    
//void *(*func)(void *):这是线程执行函数,也称为线程函数。它是一个函数指针,其类型为 void* (*)(void*)。线程函数接受一个 void* 类型的参数,并返回一个 void* 类型的值。线程函数是线程执行的核心,是线程创建的目的。
    
//void *arg:这是传递给线程函数的参数。它是一个 void* 类型的指针,你可以通过它向线程函数传递数据

 int pthread_join(pthread_t tid,void **status);
//用于等待一个指定的线程结束,并获取该线程的终止状态。在多线程程序中,主线程有时需要等待其他线程完成其工作,这时就可以使用 pthread_join() 函数。
被等待线程必须是当前进程的成员不能是分离的线程和守护线程
pthread_join() 函数的返回值如果线程正常结束返回 0如果线程已退出但尚未加入返回 EINVAL如果线程尚未结束返回 EBUSY如果出现其他错误返回相应的错误码pthread_join() 函数的作用包括等待指定的线程结束获取线程的终止状态在线程结束之前阻止调用 pthread_join() 的线程的执行

互斥锁

linux中提供一种基本的进程同步机制-互斥锁,用于保护现成代码中共享数据的完整性。

只有一个线程能加锁,其他线程只能等待解锁后才能加锁

pthread_mutex_lock();
pthread_mutex_unlock()

多线程服务器中ARG传递参数

见书p70

多线程安全

以线程特定数据TSD取代静态变量,他是线程私有的全局数据

int pthread_key_create(ptread_key_t *key,void(*destructor)(void *value));
进程中分配一个键字关键字是进程内部唯一的用来标识线程专用数据项

int pthread_setspecific(pthread_key_t key, const void *value);
该函数为TSD关键字绑定关联一个与本线程相关的值

void *pthread_getspecific(pthread_key_t key);
该函数获得与调用线程相关的关键字所绑定的值

int pthread_key_delete(pthread_key_t key);
该函数删除进程内的TSD表示的关键字该函数既不检查TSD是否有绑定值也不调用该关
键字的析构函数

IO编程