Head First C 第十一章 网络与套接字 创建knock-knock 服务器 part I

原创
2016/04/24 12:42
阅读数 54

Head First C 第十一章 网络与套接字 创建knock-knock 服务器 part I

关于套接字

套接字是一种数据流。

用途:不同计算机通过互联网进行通信。

服务器可以与多个客户端进行通信,客户端与服务器将展开一段结构化对话,被称为协议。协议通畅有一套严格的规则,客户端和服务器都严格遵守这套规则就没事,只要其中一方违反了规则,对话就会戛然而止。

BLAB:服务器连接网络四部曲

为了与外界沟通,C程序用数据流读写字节。到目前为止,我们使用过文件,标准输入,标准输出三种数据流。如果想与网络通信,就需要引入一种新的数据流——套接字

在使用套接字与客户端程序通信前,服务器要经历四个阶段:绑定(Bind)、监听(Listen)、接受(Accept)、开始(Begin),首字母缩写为BLAB。

绑定端口

计算机可能运行多个服务器程序,为了防止不同对话发生混淆,每项服务必须使用不同的端口。服务器在启动时,需要告诉系统要使用哪个端口,这个过程叫做端口绑定,knock-knock服务器将使用30000端口,为了绑定它,我们需要两样东西:套接字描述符套接字名。套接字名是一个表示“互联网30000端口”的结构。

  1. 创建套接字描述符

    
    	  int listener_d = socket(PF_INET, SOCK_STREAM, 0);
      if (listener_d == -1) {
        error("Can't open socket");
      }
      int reuse = 1;
    	  if (setsockopt(listener_d, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse,
                 sizeof(int)) == -1)
      error("Can't set the reuse option on the socket");
    
    

    这步操作是要打开套接字,socket函数接收3个参数

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

    分别是:域、类型和协议,我们选用的PF_INET表示使用ipv4协议族,SOCKADDR_STREAM表示序列化的、可靠的、双向通信的字节流。0代表的是协议,协议用来制定一个特定的用于当前socket的协议,通常只用已有的协议族里的一个协议。

    另外,绑定端口有延时:当你在某个端口绑定了套接字,在接下来的30秒内,操作系统不允许任何程序再绑定它,包括上一次绑定这个端口的程序。所以当你关闭服务器再立即重启,在很多系统下很可能会失败。所以我们要设置reuse:

    
    	  setsockopt(listener_d, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse,sizeof(int))
    
    

    该函数接收5个参数,分别是:套接字描述符、级别、设置选项、要设置的值和选项长度。

  2. 创建套接字名

    
    	  struct sockaddr_in name;
    	  name.sin_family = PF_INET;
    	  name.sin_port = (in_addr_t)htons(30000);
    	  name.sin_addr.s_addr = htonl(INADDR_ANY);
    
    

    创建一个sockaddr_in套接字,然后指定协议,端口。

  3. 绑定

    
    	  int c = bind(listener_d, (struct sockaddr *)&name, sizeof(name));
      if (c == -1)
      error("Can't bind port");
    
    

    绑定函数接收套接字描述符、套接字名的地址,以及套接字名的长度。

监听端口

如果有多个客户端来连接你的服务器,那么客户端需要排队,可以用listen()来告诉系统希望队列有多长:

  if (listen(listener_d, 10) == -1)
    error("Can't listen port");
  puts("waiting for connection...");
接受连接

一旦绑定端口,设置完等待队列,唯一可以做的就是等待。accept()系统调用会一直等待,直到有客户端连接服务器时,它会返回第二个套接字描述符,然后就可以用它通信了。

  struct sockaddr_storage client_addr;
  unsigned int address_size = sizeof(client_addr);
  int connect_d = accept(listener_d, (struct sockaddr *)&client_addr, &address_size);
  if (connect_d == -1)
    error("Can't open storage socket");

accept()系统调用接收三个参数:已创建并绑定端口的socket、连接实体的地址和连接实体的长度。

开始通信

套接字不是传统意义上的数据流

到目前为止,我们见过的数据流都一样,不管是连接文件的数据流,还是标准输入输出数据流,都可以用fprintf()fscanf()和它们通信。但套接字有一点点不同,它既可以作为输入,也可以作为输出,也就是说,要用其它函数与它通信。

如果想向套接字输出数据,就要用send()函数:

  char *msg = "Internet Knock-Knock Protocol Server\r\nVersion 1.0\r\nKnock!Knock!\r\n>";
  if(send(connect_d, msg, strlen(msg), 0) == -1)
    error("Send");

一定要检查系统调用的返回值,send()也不例外。网络错误随处可见,服务器必须处理它们。

代码

advice server sample

展开阅读全文
打赏
1
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
1
分享
返回顶部
顶部