首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

unix网络编程各种TCP客户-服务器程序设计范例(二)

2012-09-06 
unix网络编程各种TCP客户-服务器程序设计实例(二)前面我们介绍了unix网络编程各种TCP客户-服务器程序设计

unix网络编程各种TCP客户-服务器程序设计实例(二)

前面我们介绍了unix网络编程各种TCP客户-服务器程序设计实例附环境搭建和编译方法

本节我们接着介绍另外的几种TCP客户-服务器程序;

第四种:TCP并发服务器,每个客户一个子线程

在我们前面的并发服务器程序例子中可以看出:父进程接受连接,派生子进程,子进程处理与客户的交互。

这种模式的问题:

fork()是昂贵的。内存映像要从父进程拷贝到子进程,所有描述字要在子进程中复制等等。

fork()子进程后,需要用进程间通信在父子进程之间传递信息。

一个进程中的所有线程共享相同的全局内存,这使得线程很容易共享信息,但是这种简易型也带来了同步问题。一个进程中的所有线程不仅共享全局变量,而且共享:

进程指令,大多数数据,打开的文件(如描述字),信号处理程序和信号处置,当前工作目录,用户ID和组ID;

所以我们才引进了线程;

unix网络编程各种TCP客户-服务器程序设计范例(二)

当一个程序由exec启动时,会创建一个称作初始线程或主线程的单个线程。额外线程则由pthread_create函数创建。

一个进程中的每个线程都由一个线程ID标识,其数据类型是pthread_t(常常是unsigned int)。如果新的线程创建成功,其ID将通过tid指针返回。

每个线程都有很多属性:优先级、起始栈大小、是否应该是一个守护线程,等等。当创建线程时我们可以通过初始化一个phread_attr_t变量说明这些属性以覆盖缺省值。

最后,当创建一个线程时,我们要说明一个它将执行的函数。线程以调用该函数开始,然后或者显示地终止(调用pthread_exit)或者隐式的终止(让函数返回)。参数时以个指针arg。

我们可以调用pthread_join等待一个线程终止。类似于waitpid。

我们必须指定要等待线程的tid。不能等待任意一个线程结束(类似于waitpid的进程ID参数为-1的情况)。

 

互斥锁:

多个线程修改一个共享变量,是最简单的问题,解决方法是用一个互斥锁(mutex)保护共享变量;只有我们持有互斥锁才能访问该变量。

unix网络编程各种TCP客户-服务器程序设计范例(二)

互斥锁是类型为pthread_mutex_t的变量。

条件变量:

互斥锁适于阻止对共享变量的同时访问,但是我们需要某种东西以使我们能够睡眠等待某种条件出现。我们需要一种方法使得主循环进入睡眠,直到有一个线程通知它某件事已就绪。条件变量加上互斥锁可以提供这种功能。互斥锁提供互斥机制,条件变量提供信号机制。

条件变量是一个pthread_cond_t类型的变量。

unix网络编程各种TCP客户-服务器程序设计范例(二)

多线程客户端程序:

 

unix网络编程各种TCP客户-服务器程序设计范例(二)

#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);}intmain(int argc, char **argv){int sockfd;struct sockaddr_inservaddr;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 */intmain(int argc, char **argv){intlistenfd, connfd;pthread_ttid;socklen_taddrlen, 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);elseerr_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函数没有变化;

好了,又介绍完了一种设计实例,明天继续,未完待续。。。




 

热点排行