unix网络编程各种TCP客户-服务器程序设计实例(二)
2012-08-21 21:42 javaspring 阅读(265) 评论(0) 编辑 收藏 举报前面我们介绍了unix网络编程各种TCP客户-服务器程序设计实例附环境搭建和编译方法
本节我们接着介绍另外的几种TCP客户-服务器程序;
第四种:TCP并发服务器,每个客户一个子线程
在我们前面的并发服务器程序例子中可以看出:父进程接受连接,派生子进程,子进程处理与客户的交互。
这种模式的问题:
fork()是昂贵的。内存映像要从父进程拷贝到子进程,所有描述字要在子进程中复制等等。
fork()子进程后,需要用进程间通信在父子进程之间传递信息。
一个进程中的所有线程共享相同的全局内存,这使得线程很容易共享信息,但是这种简易型也带来了同步问题。一个进程中的所有线程不仅共享全局变量,而且共享:
进程指令,大多数数据,打开的文件(如描述字),信号处理程序和信号处置,当前工作目录,用户ID和组ID;
所以我们才引进了线程;
当一个程序由exec启动时,会创建一个称作初始线程或主线程的单个线程。额外线程则由pthread_create函数创建。
一个进程中的每个线程都由一个线程ID标识,其数据类型是pthread_t(常常是unsigned int)。如果新的线程创建成功,其ID将通过tid指针返回。
每个线程都有很多属性:优先级、起始栈大小、是否应该是一个守护线程,等等。当创建线程时我们可以通过初始化一个phread_attr_t变量说明这些属性以覆盖缺省值。
最后,当创建一个线程时,我们要说明一个它将执行的函数。线程以调用该函数开始,然后或者显示地终止(调用pthread_exit)或者隐式的终止(让函数返回)。参数时以个指针arg。
我们可以调用pthread_join等待一个线程终止。类似于waitpid。
我们必须指定要等待线程的tid。不能等待任意一个线程结束(类似于waitpid的进程ID参数为-1的情况)。
互斥锁:
多个线程修改一个共享变量,是最简单的问题,解决方法是用一个互斥锁(mutex)保护共享变量;只有我们持有互斥锁才能访问该变量。
互斥锁是类型为pthread_mutex_t的变量。
条件变量:
互斥锁适于阻止对共享变量的同时访问,但是我们需要某种东西以使我们能够睡眠等待某种条件出现。我们需要一种方法使得主循环进入睡眠,直到有一个线程通知它某件事已就绪。条件变量加上互斥锁可以提供这种功能。互斥锁提供互斥机制,条件变量提供信号机制。
条件变量是一个pthread_cond_t类型的变量。
多线程客户端程序:
#include "unpthread.h" void* copyto(void*); static int sockfd;//全局变量,所有线程共有 static FILE *fp; void str_cli1(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); } int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr; if (argc != 2) err_quit("usage: tcpcli <IPaddress>"); sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); Inet_pton(AF_INET, argv[1], &servaddr.sin_addr); Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); str_cli1(stdin, sockfd); /* do it all */ exit(0); }
服务器程序:
#include "unpthread.h" static void *doit(void *); /* each thread executes this function */ int main(int argc, char **argv) { int listenfd, 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 [ <host> ] <service or port>"); cliaddr = Malloc(addrlen); for ( ; ; ) { len = addrlen; connfd = Accept(listenfd, cliaddr, &len); Pthread_create(&tid, NULL, &doit, (void *) connfd); } } static void * doit(void *arg) { Pthread_detach(pthread_self()); str_echo((int) arg); /* same function as before */ Close((int) arg); /* done with connected socket */ return(NULL); }
注意:这里我们编译的时候,由于pthread不是系统默认的库,所以要用命令:
gcc tcpcli.c -o client -lunp -lpthread
gcc tcpserv.c -o server -lunp -lpthread
服务器运行命令:sudo ./server 127.0.0.1 9877
客户端运行命令:./client 127.0.0.1
我们注意到每种客户端的程序其实只要是str_cli函数的改变,main函数没有变化;
好了,又介绍完了一种设计实例,明天继续,未完待续。。。