客户端-服务器编程模型

每个网络应用都是基于客户端-服务器模型的。基于这个模型,一个应用是由一个服务器进程和一个或多个客户端进程组成。服务器管理者某种资源,并利用这种资源为其他客户端提供服务。 常见的类型有:web服务器FTP服务器电子邮件服务器等。

套接字编程

套接字接口流程图

socket

套接字函数

int socket(int domain,int type,int protocol)

如果想要使套接字成为连接的一个端点,使用 clinetfd = Socket(AF_INET,SOCK_STREAM,0),AF_INET代表正在使用32位IP地址,而SOCK_STREAM表示这个套接字是连接的一个端点。socket返回的clientfd描述符仅是部分打开的,还不能用于读写。

connect函数

客户端通过调用connect函数来建立和服务器的连接。

int connect(int clientfd,const struct sockaddr *addr,socklen_t addr)

connect函数试图与套接字地址为addr的服务器建立一个因特网连接,其中addrlen是sizeof(sockaddr_in)。connect函数会阻塞,一直到连接成功建立或者发生错误。如果成功,clientfd描述符现在就可以准备好读写了。

bind函数

服务器用bind、listen和accept等套接字来和客户端建立连接。

int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen

bind函数告诉内核将addr中的服务器套接字地址和套接字描述符sockfd联系起来。

listen函数

int listen(int socktd,int backlog)

listen函数将sockfd从一个主动套接字转化为一个监听套接字(listening socket),该套接字可以接受来自客户端的连接请求。backlog参数暗示了内核在开始拒绝连接请求之前,队列中要排队的未完成的连接请求的数量。

accept函数

int accept(int listenfd,struct sockaddr *addr,int *addrlen)

accept函数等待来自客户端的请求连接到达侦听描述符listenfd,然后在addr中填写客户端的套接字地址,并返回一个已连接描述符,这个描述符可被用来利用Unix I/O函数与客户端通信。

监听描述符vs已连接描述符:监听描述符是作为客户端连接请求中的一个端点。它通常被创建一次,并存在于服务器的整个生命周期。已连接描述符是客户端和服务器之间已经建立起来的一个端点。服务器每次接受连接请求时都会创建一次,它只存在服务器为一个客户端服务的过程中。 socket

套接字接口区分监听描述符和已连接描述符的一个重要的原因就是:使用连接描述符可以建立并发服务器,它能够同时处理许多客户端连接。例如:每次一个连接请求到达监听描述符时,可以派生出一个新的进程,它通过已连接描述符和客户端通信。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
//简单的单连接无并发的程序
void echo(int connfd);
int main(int argc,char **argv)
{
  int listenfd,connfd;
  socklen_t clientfd;
  struct sockaddr_storage clientaddr;
  char client_hostname[MAXLINE],client_port[MAXLINE];
  
  if (argc!=2){
    exit(0);
  }
  
  listenfd = Open_listenfd(argv[1]);
  while(1){
    clientlen = sizeof(struct sockaddr_storage);
    connfd = Accept(listenfd,(SA *)&clientaddr, &clientlen);
    echo(connfd);
    Close(connfd);
  }
  exit(0);
}