码迷,mamicode.com
首页 > 系统相关 > 详细

五十四、linux 编程——TCP 编程模型

时间:2019-02-23 01:13:20      阅读:243      评论:0      收藏:0      [点我收藏+]

标签:signal   image   调用   启动   height   介绍   out   sage   erro   

54.1 编程模型介绍

54.1.1 TCP 客户端服务器编程模型

  技术图片

  • 客户端调用序列
    • 调用 socket 函数创建套接字
    • 调用 connect 连接服务器端
    • 调用 I/O 函数(read/write) 与服务器端通讯
    • 调用 close 关闭套接字
  • 服务器端调用序列
    • 调用 socket 函数创建套接字
    • 调用 bind 绑定本地地址和端口
    • 调用 listen 启动监听
    • 调用 accept 从已连接队列中提取客户连接
    • 调用 I/O 函数(read/write)与客户端通讯
    • 调用 close 关闭套接字

 54.1.2 套接字与地址绑定

  sockaddr 为自定义的结构体,示例如下:

  技术图片

(1)绑定地址

  技术图片

  • 函数返回值:成功,则返回 0;出错,则返回 -1

(2)查找绑定到套接字的地址

  技术图片

  • 返回值:成功,则返回 0;出错,则返回 -1

(3)获取对方地址

   技术图片

  • 返回值:成功,则返回 0;出错, 则返回 -1

 (4)建立连接

  服务器端:

  技术图片

  • 返回:成功返回0;出错返回 -1.
  • 说明:backlog 指定进行客户端连接排队的队列长度

  技术图片

  • 函数功能:获取客户端的连接
  • 函数参数:
    • address:通用地址,可以存放来源于客户端的地址信息,若不想获取客户端的信息,设置为NULL
  • 返回值:

  客户端:

  技术图片

  • 返回:成功返回0;出错返回 -1

54.1.3 特殊 bind 地址

  • 一台主机可以有多个网络接口和多个 IP 地址,如果我们只关心某个地址的连接请求,我们可以指定一个具体的本地 IP 地址,如果要响应所有接口上的连接请求,就要使用一个特殊的地址 INADDR_ANY
  • #define INADDR_ANY (uint32_t)0x00000000

  技术图片

54.2 TCP 编程例子

  客户端连接到服务器端后,服务器端返回给客户端一个系统时间,客户端将此时间打印出来

54.2.1 服务器端编程

       time_tcp_server.c

  1 #include <netdb.h>
  2 #include <netinet/in.h>
  3 #include <sys/socket.h>
  4 #include <unistd.h>
  5 #include <string.h>
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <memory.h>
  9 #include <signal.h>
 10 #include <time.h>
 11 #include <arpa/inet.h>
 12 
 13 
 14 int sockfd;
 15 
 16 void sig_handler(int signo)
 17 {
 18     if(signo == SIGINT){
 19         printf("server close\n");
 20         /** 步骤6: 关闭 socket */
 21         close(sockfd);
 22         exit(1);
 23     }
 24 }
 25 
 26 /** 输出连接上来的客户端相关信息 */
 27 void out_addr(struct sockaddr_in *clientaddr)
 28 {
 29     /** 将端口从网络字节序转换成主机字节序 */
 30     int port = ntohs(clientaddr->sin_port);
 31     char ip[16];
 32     memset(ip, 0, sizeof(ip));
 33     /** 将 ip 地址从网络字节序转换成点分十进制 */
 34     inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));
 35     printf("client: %s(%d) connected\n", ip, port);
 36 }
 37 
 38 void do_service(int fd)
 39 {
 40     /** 获得系统时间 */
 41     long t = time(0);
 42     char *s = ctime(&t);
 43     ssize_t size = strlen(s) * sizeof(char);
 44 
 45     /** 将服务器获得的系统时间写回到客户端 */
 46     if(write(fd, s, size) != size){
 47         perror("write error");
 48     }
 49 }
 50 
 51 int main(int argc, char *argv[])
 52 {
 53     if(argc < 2){
 54         printf("usage: %s #port\n", argv[0]);
 55         exit(1);
 56     }
 57 
 58     if(signal(SIGINT, sig_handler) == SIG_ERR){
 59         perror("signal sigint error");
 60         exit(1);
 61     }
 62 
 63     /** 步骤1: 创建 socket(套接字) 
 64      *  注: socket 创建在内核中,是一个结构体.
 65      *  AF_INET: IPV4
 66      *  SOCK_STREAM: tcp 协议
 67      *  AF_INET6: IPV6
 68      */
 69     sockfd = socket(AF_INET, SOCK_STREAM, 0);
 70 
 71     /** 
 72      * 步骤2: 调用 bind 函数将 socket 和地址(包括 ip、port)进行绑定
 73      */
 74     struct sockaddr_in  serveraddr;
 75     memset(&serveraddr, 0, sizeof(struct sockaddr_in));
 76     /** 往地址中填入 ip、port、internet 地址族类型 */
 77     serveraddr.sin_family = AF_INET;    ///< IPV4
 78     serveraddr.sin_port = htons(atoi(argv[1])); ///< 填入端口
 79     serveraddr.sin_addr.s_addr = INADDR_ANY; ///< 填入 IP 地址
 80     if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in))){
 81         perror("bind error");
 82         exit(1);
 83     }
 84 
 85     /**
 86      *  步骤3: 调用 listen 函数启动监听(指定 port 监听)
 87      *         通知系统去接受来自客户端的连接请求
 88      *         (将接受到的客户端连接请求放置到对应的队列中)
 89      *  第二个参数: 指定队列的长度
 90      */
 91     if(listen(sockfd, 10) < 0){
 92         perror("listen error");
 93         exit(1);
 94     }
 95 
 96     /**
 97      *  步骤4: 调用 accept 函数从队列中获得一个客户端的请求连接, 并返回新的
 98      *         socket 描述符
 99      *  注意:  若没有客户端连接,调用此函数后会足则, 直到获得一个客户端的连接
100      */
101     struct sockaddr_in clientaddr;
102     socklen_t clientaddr_len = sizeof(clientaddr);
103     while(1){
104         int fd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len);
105         if(fd < 0){
106             perror("accept error");
107             continue;
108         }
109 
110         /**
111          *  步骤5: 调用 IO 函数(read/write)和连接的客户端进行双向的通信
112          */
113         out_addr(&clientaddr);
114         do_service(fd);
115 
116         /** 步骤6: 关闭 socket */
117         close(fd);
118     }
119 
120     return 0;
121 }

  编译测试:

  技术图片

  可以看到,另一个终端返回了系统时间。

54.2.2 客户端编程

  time_tcp_client.c

 1 #include <sys/types.h>
 2 #include <stdlib.h>
 3 #include <stdio.h>
 4 #include <memory.h>
 5 #include <unistd.h>
 6 #include <sys/socket.h>
 7 #include <netdb.h>
 8 #include <signal.h>
 9 #include <string.h>
10 #include <time.h>
11 #include <arpa/inet.h>
12 
13 
14 int main(int argc, char *argv[])
15 {
16     if(argc < 3){
17         printf("usage: %s ip port\n", argv[0]);
18         exit(1);
19     }
20 
21     /** 步骤1: 创建 socket */
22     int sockfd = socket(AF_INET, SOCK_STREAM, 0);
23     if(sockfd < 0){
24         perror("socket error");
25         exit(1);
26     }
27 
28     /** 往 serveraddr 中填入 ip、port 和地址族类型(ipv4) */
29     struct sockaddr_in serveraddr;
30     memset(&serveraddr, 0, sizeof(struct sockaddr_in));
31     serveraddr.sin_family = AF_INET;
32     serveraddr.sin_port = htons(atoi(argv[2]));
33     /** 将 ip 地址转换成网络字节序后填入 serveraddr 中  */
34     inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr);
35 
36     /**
37      *  步骤2: 客户端调用 connect 函数连接到服务器端
38      */
39     if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)) < 0){
40         perror("connect error");
41         exit(1);
42     }
43 
44     /** 步骤3: 调用 IO 函数(read/write)和服务器端进行双向通信 */
45     char buffer[1024];
46     memset(buffer, 0, sizeof(buffer));
47     ssize_t size;
48     if((size = read(sockfd, buffer, sizeof(buffer))) < 0){
49         perror("read error");
50     }
51     if(write(STDIN_FILENO, buffer, size) != size){
52         perror("write error");
53     }
54 
55     /** 步骤4: 关闭 socket */
56     close(sockfd);
57 
58     return 0;
59 }

  编译在两个终端上,一个打开服务器,一个打开客户端测试:

  技术图片

五十四、linux 编程——TCP 编程模型

标签:signal   image   调用   启动   height   介绍   out   sage   erro   

原文地址:https://www.cnblogs.com/kele-dad/p/10404173.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!