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

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 }

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

  

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

时间: 2024-11-09 03:52:06

五十四、linux 编程——TCP 编程模型的相关文章

QT开发(三十四)——QT多线程编程

QT开发(三十四)--QT多线程编程 一.QT多线程简介 QT通过三种形式提供了对线程的支持,分别是平台无关的线程类.线程安全的事件投递.跨线程的信号-槽连接. QT中线程类包含如下: QThread 提供了开始一个新线程的方法    QThreadStorage 提供逐线程数据存储    QMutex 提供相互排斥的锁,或互斥量    QMutexLocker 是一个辅助类,自动对 QMutex 加锁与解锁    QReadWriterLock 提供了一个可以同时读操作的锁    QReadL

QT开发(五十四)———QML组件

QT开发(五十四)---QML组件 QML组件是由基本元素组合成的一个复杂的可重用的组合元素.QML 提供了多种方法来创建组件. 基于文件的组件将QML元素放置在一个单独的文件中,然后给文件一个名字,可以通过名字来使用组件.如果有一个文件名为Cell.qml,就可以在QML中使用Cell { - }形式.自定义组件的文件名的首字母必须大写. Cell.qml文件: import QtQuick 2.0   Item {     id: container     property alias c

第三百五十四节,Python分布式爬虫打造搜索引擎Scrapy精讲—数据收集(Stats Collection)

第三百五十四节,Python分布式爬虫打造搜索引擎Scrapy精讲-数据收集(Stats Collection) Scrapy提供了方便的收集数据的机制.数据以key/value方式存储,值大多是计数值. 该机制叫做数据收集器(Stats Collector),可以通过 Crawler API 的属性 stats 来使用无论数据收集(stats collection)开启或者关闭,数据收集器永远都是可用的. 因此您可以import进自己的模块并使用其API(增加值或者设置新的状态键(stat k

“全栈2019”Java第五十四章:多态详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第五十四章:多态详解 下一章 "全栈2019"Java第五十五章:方法的静态绑定与动态绑定 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习小组"

Linux 网络编程——TCP编程

概述 TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议. TCP 具有以下特点: 1)电话系统服务模式的抽象 2)每一次完整的数据传输都要经过建立连接.使用连接.终止连接的过程 3)可靠.出错重传.且每收到一个数据都要给出相应的确认,保证数据传输的可靠性 TCP 编程的 C/S 架构 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: TCP 客户端编程 所需头文件:#inclu

学习五十四

十四周四次课(5月14日)16.1 Tomcat介绍16.2 安装jdk16.3 安装Tomcat 扩展java容器比较 http://my.oschina.net/diedai/blog/271367 http://www.360doc.com/content/11/0618/21/16915_127901371.shtmlj2ee.j2se.ejb.javabean.serverlet.jsp之间关系 http://bbs.csdn.net/topics/50015576tomcat ser

五十四 网络编程 TCP编程

Socket是网络编程的一个抽象概念.通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可. 客户端 大多数连接都是可靠的TCP连接.创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器. 举个例子,当我们在浏览器中访问新浪时,我们自己的计算机就是客户端,浏览器会主动向新浪的服务器发起连接.如果一切顺利,新浪的服务器接受了我们的连接,一个TCP连接就建立起来的,后面的通信就是发送网页内容了. 所以,我们要

C#高级编程五十四天----Lookup类和有序字典

Lookup类 Dictionary<Tkey,TValue>只为每个键支持一个值.新类Lookup<Tkey,TValue>是.NET3.5中新增的,它类似与Dictionary<Tkey,TElement>,但把键映射带一个值集上.这个类在程序及System.Core中实现,用System,Linq命名空间定义. Lookup<Tkey,TElement>的方法和属性如下表: 属性名或者方法名 说明 Count 属性Count返回集合中的元素个数 Ite

C#编程(五十四)----------Lookup类和有序字典

原文链接: http://blog.csdn.net/shanyongxu/article/details/47071607 Lookup类 Dictionary<Tkey,TValue>只为每个键支持一个值.新类Lookup<Tkey,TValue>是.NET3.5中新增的,它类似与Dictionary<Tkey,TElement>,但把键映射带一个值集上.这个类在程序及System.Core中实现,用System,Linq命名空间定义. Lookup<Tkey