UNP学习笔记二--简单的并发服务器(concurrent servers)

1、一个典型的tcp client/server调用的函数顺序如下:

2、tcp server会在accept处等待客户端连接;而udp server则会在recvfrom函数处等待客户端连接。

3、一个简单的并发服务器使用fork和exec来完成客户端的并发请求处理。fork以后,父进程关闭connect fd,子进程关闭listen fd,子进程在完成逻辑处理后关闭 connent fd。

#include        "unp.h"

int main(int argc, char **argv)
{
        int                                     listenfd, connfd;
        pid_t                           childpid;
        socklen_t                       clilen;
        struct sockaddr_in      cliaddr, servaddr;

        listenfd = Socket(AF_INET, SOCK_STREAM, 0);

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family      = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port        = htons(SERV_PORT);

        Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

        Listen(listenfd, LISTENQ);

        for ( ; ; ) {
                clilen = sizeof(cliaddr);
                connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

                if ( (childpid = Fork()) == 0) {        /* child process */
                        Close(listenfd);        /* close listening socket */
                        str_echo(connfd);       /* process the request */
                        exit(0);
                }
                Close(connfd);                  /* parent closes connected socket */
        }
}

 

以下小结来自:http://blog.chinaunix.net/u/21000/showart.php?id=143807

面向连接的编程模式——TCP套接字

服务器端编程模板:

socket();   //创建套接字,同时也是一个文件描述符

bind();     //邦定套接字和服务器端口(包括IP地址和端口号),邦定之前要先初始化IP地址和端口

listen();   //监听该端口

accept();   //一般在一个while(1)循环体里。如果有连接请求则接受请求,返回一个新的与客户端绑定了的套接字(称为连接套接字,与前面的监听套接字相对),并产生一个新的进程或线程处理该客户的请求

recv() or send();//与客户端交换数据,或处理程序,一般在新产生的的进程或线程里的一个循环体里

close();    //别忘了关闭套接字,当然,如果中途发生错误,退出之前也应该关闭套接字

客户端模版:

socket();

connect();  //向客户端发送连接请求,客户端是不用邦定的,由系统自动为套接字分配端口,该函数把本地IP和该线程的端口发送给服务器

send() or recv(); //与服务器交换数据

close();          //同上

面向无连接的编程模式——UDP套接字

服务器端编程模版:

socket();

bind();      //绑定监听端口

recvfrom();  //堵塞,直到接收到数据报

sendto();    //处理客户数据或作出回应

close();

客户端编程模版:

socket();

sendto();

recvfrom();  //堵塞,直到接收到回应,还要判断是不是特定服务器的回应,一般是在一个while循环里。

close();

对比TCP和UDP,前者需要一个特定的端口作为监听端口,专门接收来自客户端的服务请求。send()中不用包含目标IP地址和端口,因为客户和服务器已经建立了连接,直接对连接套接口操作就可以了,而recv()也不用存放信息的IP地址和端口。而后者不需要监听端口,只需要一个端口接收数据报就可以了,但sendto()需要目标IP地址和端口,而recv()也需要存放IP和端口的空间。

另外,应用UDP的客户端还需要判断接收到的数据是不是特定服务器的回复,这需要比较IP地址和端口。

并发的实现方法

应用多进程:

当accept()接收到新的连接请求,应用fork()系统调用,产生子进程处理客户请求。

fork()函数原型:

#include <sys/types.h>

#include <unistd.h>

pid_t fork(void)

返回值:子进程为0,父进程为子进程的ID,出错为-1

子进程是父进程的副本,子进程中有父进程的数据空间、堆栈的一份copy,注意是copy而不是共享,这是多进程与多线程最主要的区别,也导致截然不同的处理方式。

一般可以根据fork()的返回值来使父、子进程进行不同的处理,如:

while (1){
if((connectSoc = accept(listenSoc,(struct sockaddr *)&client, &sin_size)) == -1){
  perror("accept() error!\n");
  close(listenSoc);
  exit(1);
}
pid = fork();
if(pid > 0){      //父进程处理程序,继续接收新的客户请求
  close(connectSoc);
  continue;
}
else if (pid == 0){//子进程处理程序
  close(listenSoc);
  printf("You got a connection from %s\n",inet_ntoa(client.sin_addr));
  if(send(connectSoc,Msg,25,0) == -1){
   perror("send() error!\n");
  }
  close(connectSoc);
  exit(1);
}
else {
  printf("fork() error!\n");
  close(listenSoc);
  exit(1);
}
}

posted @ 2009-06-14 22:20  一只灰色的羊  阅读(730)  评论(1编辑  收藏  举报