作者: 赵晓伟 | 来源:互联网 | 2023-01-28 14:41
在非阻塞式IO一节中,我们使用fork实现了一个多进程TCP回射客户程序,使得父进程处理标准输入到套接字的数据,子进程处理套接字到标准输出的数据。voidstr_cli(FILE*fp,int
在非阻塞式I/O一节中,我们使用fork实现了一个多进程TCP回射客户程序,使得父进程处理标准输入到套接字的数据,子进程处理套接字到标准输出的数据。
void str_cli(FILE *fp, int sockfd)
{
pid_t pid;
char sendline[MAXLINE], recvline[MAXLINE];
/*子进程 socket ---> stdout*/
if ((pid = Fork()) == 0) {
while (Readline(sockfd, recvline, MAXLINE) > 0)
Fputs(recvline, stdout);
/*子进程向父进程发送一个SIGTERM信号,以防止父进程继续运行*/
kill(getppid(), SIGTERM);
exit(0);
}
/*父进程stdin ---> socket*/
while (Fgets(sendline, MAXLINE, fp) != NULL)
Writen(sockfd, sendline, strlen(sendline));
Shutdown(sockfd, SHUT_WR);
pause();
return;
}
然而,这个程序有两个缺点:
1. fork()调用非常昂贵,它要复制一个进程。
2. 父子进程之间传递信息需要进程间通信(IPC)机制。
使用线程(也称作“轻量级进程”),我们就能避免这两个问题。一是因为创建一个线程往往比创建一个进程花费的时间要少的多,二是因为同一个进程内的所有线程都共享全局内存,这使得线程间通信非常容易。然而,易于共享信息也使得线程要面临我们后面要讲的“同步”问题。
下面给出Linux下线程库pthread常用的API:
#include
/*创建一个线程*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
/*等待一个线程退出*/
int pthread_join(pthread_t thread, void **retval);
/*使线程转为脱离状态*/
int pthread_detach(pthread_t thread);
/*返回线程ID*/
pthread_t pthread_self(void);
/*退出线程*/
void pthread_exit(void *value_ptr);
下面是相应的str_cli函数的多线程版本:
#include "unp.h"
void *copyto(void *);
static int sockfd;
static FILE *fp;
void str_cli(FILE *fp_arg, int sockfd_arg)
{
char recvline[MAXLINE];
pthread_t tid;
sockfd = sockfd_arg;
fp = fp_arg;
/*创建一个线程*/
Pthread_create(&tid, NULL, copyto, NULL);
while (Readline(sockfd, recvline, MAXLINE) > 0)
Fputs(recvline, stdout);
}
void *copyto(void *arg)
{
char sendline[MAXLINE];
while (Fgets(sendline, MAXLINE, fp) != NULL)
Writen(sockfd, sendline, strlen(sendline));
Shutdown(sockfd, SHUT_WR);
return NULL;
}
下面是使用多线程的TCP回射服务器程序:
#include "unp.h"
static void *doit(void *);
int main(int argc, char **argv)
{
int listenfd, *iptr; /*connfd;*/
pthread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;
if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 3)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
else
err_quit("usage: tcpserv01 [ ] ");
cliaddr = Malloc(addrlen);
for ( ; ; ) {
len = addrlen;
iptr = Malloc(sizeof(int));
*iptr = Accept(listenfd, cliaddr, &len);
Pthread_create(&tid, NULL, &doit, iptr); /*为每个客户创建一个服务线程*/
}
}
static void *doit(void *arg)
{
int connfd;
cOnnfd= *((int*)arg);
free(arg);
/*调用pthread_detach函数让自身脱离,因为
主线程没有理由等待它创建的每个线程结束*/
Pthread_detach(pthread_self());
str_echo(connfd);
Close(connfd);
return NULL;
}
注意是如何将描述符通过参数传递给线程函数的。