码字pthread

多线程tcp实例

服务端:

/************pthread_cond_wait()的使用方法**********/                                                                                                                                            
//pthread_mutex_lock(&qlock);    /*lock*/
//pthread_cond_wait(&qready, &qlock); /*block-->unlock-->wait() return-->lock*/
//pthread_mutex_unlock(&qlock); /*unlock*/
/*****************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
 
void* testThreadPool(int *t);
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITIALIZER;
int a = 0;
 
int main() {
  int sock_fd, conn_fd;
  int optval;
 
  socklen_t cli_len;
  struct sockaddr_in cli_addr, serv_addr;
 
 
  sock_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (sock_fd < 0) {
       printf("socket\n");
 
  }
 
  optval = 1;
  if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval,
        sizeof(int)) < 0) {
       printf("setsockopt\n");
 
  }
 
  memset(&serv_addr, 0, sizeof(struct sockaddr_in));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(6666);
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
  if (bind(sock_fd, (struct sockaddr *) &serv_addr,
        sizeof(struct sockaddr_in)) < 0) {
       printf("bind\n");

       
  }    
       
  if (listen(sock_fd, 100) < 0) {
       printf("listen\n");
       
  }    
       
  cli_len = sizeof(struct sockaddr_in);
  int t;
  pthread_t * mythread;
  mythread = (pthread_t*) malloc(100 * sizeof(pthread_t));
  for (t = 0; t < 5; t++) {
    int *i=(int*)malloc(sizeof(int));
    *i=t;
    if (pthread_create(&mythread[t], NULL, (void*)testThreadPool, (void*)i) != 0) {
      printf("pthread_create");
    }
  }
 
  while (1) {
       conn_fd = accept(sock_fd, (struct sockaddr *) &cli_addr, &cli_len);
       if (conn_fd < 0) {
             printf("accept\n");
 
       }
       printf("accept a new client, ip:%s\n", inet_ntoa(cli_addr.sin_addr));
 
       pthread_mutex_lock(&clifd_mutex);
       a=conn_fd;
       pthread_cond_signal(&clifd_cond);
       pthread_mutex_unlock(&clifd_mutex);
 
  }
  return 0;
}
 
void* testThreadPool(int *t) {
  printf("t is %d\n", *t);
  for (;;) {
       pthread_mutex_lock(&clifd_mutex);
       pthread_cond_wait(&clifd_cond, &clifd_mutex);
       printf("a is %d\n", a);
       printf("t is %d\n", *t);
       pthread_mutex_unlock(&clifd_mutex);
       sleep(100);
  }
  return (void*) 0;
 

客户端:

#include<stdio.h>
#include<stdlib.h>  
#include<string.h>
#include<errno.h>                              
#include<sys/types.h>
#include<sys/socket.h>      
#include<netinet/in.h>      
                                                
#define MAXLINE 4096        

int main(int argc, char** argv)
{
      int    sockfd, n;
      char   recvline[4096], sendline[4096];
      struct sockaddr_in    servaddr;

      if( argc != 2 ){
            printf("usage: ./client <ipaddress>/n");
            exit(0);
                     
      }

      if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ){
            printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
            exit(0);
                     
      }

      memset(&servaddr, 0, sizeof(servaddr));
      servaddr.sin_family = AF_INET;
      servaddr.sin_port = htons(6666);
      if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0 ){
            printf("inet_pton error for %s\n",argv[1]);
            exit(0);
                     
      }

      if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0 ){
            printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
            exit(0);
                     
      }

      printf("send msg to server.\n");
      fgets(sendline, 4096, stdin);
      if( send(sockfd, sendline, strlen(sendline), 0) < 0 )
      {
            printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
            exit(0);
                     
      }

      close(sockfd);
      exit(0);
                                               
}

服务端抓包:

tcpdump -i eth0 -Nnn -s0 -v port 6666
17:37:42.343179 IP (tos 0x60, ttl 58, id 52630, offset 0, flags [DF], proto TCP (6), length 60)
    10.66.130.25.39508 > 192.168.168.161.6666: Flags [S], cksum 0x6c35 (correct), seq 2610669041, win 29200, options [mss 1460,sackOK,TS val 2302866662 ecr 0,nop,wscale 7], length 0
17:37:42.343257 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.168.161.6666 > 10.66.130.25.39508: Flags [S.], cksum 0xf5d3 (incorrect -> 0xefa3), seq 3774667452, ack 2610669042, win 28960, options [mss 1460,sackOK,TS val 3754877415 ecr 2302866662,nop,wscale 7], length 0
17:37:42.344247 IP (tos 0x60, ttl 58, id 52631, offset 0, flags [DF], proto TCP (6), length 52)
    10.66.130.25.39508 > 192.168.168.161.6666: Flags [.], cksum 0x8eaa (correct), ack 1, win 229, options [nop,nop,TS val 2302866663 ecr 3754877415], length 0
17:37:43.462528 IP (tos 0x60, ttl 58, id 52632, offset 0, flags [DF], proto TCP (6), length 53)
    10.66.130.25.39508 > 192.168.168.161.6666: Flags [P.], cksum 0x8042 (correct), seq 1:2, ack 1, win 229, options [nop,nop,TS val 2302867782 ecr 3754877415], length 1
17:37:43.462552 IP (tos 0x0, ttl 64, id 15189, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.168.161.6666 > 10.66.130.25.39508: Flags [.], cksum 0xf5cb (incorrect -> 0x85ec), ack 2, win 227, options [nop,nop,TS val 3754878535 ecr 2302867782], length 0
17:37:43.462568 IP (tos 0x60, ttl 58, id 52633, offset 0, flags [DF], proto TCP (6), length 52)
    10.66.130.25.39508 > 192.168.168.161.6666: Flags [F.], cksum 0x8a49 (correct), seq 2, ack 1, win 229, options [nop,nop,TS val 2302867782 ecr 3754877415], length 0
17:37:43.502334 IP (tos 0x0, ttl 64, id 15190, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.168.161.6666 > 10.66.130.25.39508: Flags [.], cksum 0xf5cb (incorrect -> 0x85c3), ack 3, win 227, options [nop,nop,TS val 3754878575 ecr 2302867782], length 0

 

pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,如果条件不成立我们就进入阻塞,但是进入阻塞这个期间,如果条件变量改变了的话,那我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁,使得其它线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又自动给mutex加锁。

 

了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。 

首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象,然而该列表恰巧是空的。这一特定线程什么也干不了 -- 其设计意图是从列表中除去节点,但是现在却没有节点。因此,它只能: 

锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。 

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。 【要求解锁并阻塞是一个原子操作

此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。 

现在继续说明,假设另一个线程(称作“2 号线程”)锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒。 

现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。 

停止并回顾! 
那个过程非常复杂,因此让我们先来回顾一下。第一个线程首先调用: 
pthread_mutex_lock(&mymutex); 
然后,它检查了列表。没有找到感兴趣的东西,于是它调用:
pthread_cond_wait(&mycond, &mymutex); 
 
然后,pthread_cond_wait() 调用在返回前执行许多操作: 
 
pthread_mutex_unlock(&mymutex); 
 
它对 mymutex 解锁,然后进入睡眠状态,等待 mycond 以接收 POSIX 线程“信号”。一旦接收到“信号”(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会苏醒。但 pthread_cond_wait() 没有立即返回 -- 它还要做一件事:重新锁定 mutex:
pthread_mutex_lock(&mymutex); 
 
pthread_cond_wait() 知道我们在查找 mymutex “背后”的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。

 

https://blog.youkuaiyun.com/dddddz/article/details/8619141

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值