标签:java unix
本节我们接着介绍另外的几种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函数没有变化;
好了,又介绍完了一种设计实例,明天继续,未完待续。。。
版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。
unix网络编程各种TCP客户-服务器程序设计实例(二)
标签:java unix
原文地址:http://blog.csdn.net/u013948191/article/details/46848481