五十四、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
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <signal.h>
#include <time.h>
#include <arpa/inet.h> int sockfd; void sig_handler(int signo)
{
if(signo == SIGINT){
printf("server close\n");
/** 步骤6: 关闭 socket */
close(sockfd);
exit();
}
} /** 输出连接上来的客户端相关信息 */
void out_addr(struct sockaddr_in *clientaddr)
{
/** 将端口从网络字节序转换成主机字节序 */
int port = ntohs(clientaddr->sin_port);
char ip[];
memset(ip, , sizeof(ip));
/** 将 ip 地址从网络字节序转换成点分十进制 */
inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));
printf("client: %s(%d) connected\n", ip, port);
} void do_service(int fd)
{
/** 获得系统时间 */
long t = time();
char *s = ctime(&t);
ssize_t size = strlen(s) * sizeof(char); /** 将服务器获得的系统时间写回到客户端 */
if(write(fd, s, size) != size){
perror("write error");
}
} int main(int argc, char *argv[])
{
if(argc < ){
printf("usage: %s #port\n", argv[]);
exit();
} if(signal(SIGINT, sig_handler) == SIG_ERR){
perror("signal sigint error");
exit();
} /** 步骤1: 创建 socket(套接字)
* 注: socket 创建在内核中,是一个结构体.
* AF_INET: IPV4
* SOCK_STREAM: tcp 协议
* AF_INET6: IPV6
*/
sockfd = socket(AF_INET, SOCK_STREAM, ); /**
* 步骤2: 调用 bind 函数将 socket 和地址(包括 ip、port)进行绑定
*/
struct sockaddr_in serveraddr;
memset(&serveraddr, , sizeof(struct sockaddr_in));
/** 往地址中填入 ip、port、internet 地址族类型 */
serveraddr.sin_family = AF_INET; ///< IPV4
serveraddr.sin_port = htons(atoi(argv[1])); ///< 填入端口
serveraddr.sin_addr.s_addr = INADDR_ANY; ///< 填入 IP 地址
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in))){
perror("bind error");
exit();
} /**
* 步骤3: 调用 listen 函数启动监听(指定 port 监听)
* 通知系统去接受来自客户端的连接请求
* (将接受到的客户端连接请求放置到对应的队列中)
* 第二个参数: 指定队列的长度
*/
if(listen(sockfd, ) < ){
perror("listen error");
exit();
} /**
* 步骤4: 调用 accept 函数从队列中获得一个客户端的请求连接, 并返回新的
* socket 描述符
* 注意: 若没有客户端连接,调用此函数后会足则, 直到获得一个客户端的连接
*/
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
while(){
int fd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len);
if(fd < ){
perror("accept error");
continue;
} /**
* 步骤5: 调用 IO 函数(read/write)和连接的客户端进行双向的通信
*/
out_addr(&clientaddr);
do_service(fd); /** 步骤6: 关闭 socket */
close(fd);
} return ;
}
编译测试:

可以看到,另一个终端返回了系统时间。
54.2.2 客户端编程
time_tcp_client.c
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h> int main(int argc, char *argv[])
{
if(argc < ){
printf("usage: %s ip port\n", argv[]);
exit();
} /** 步骤1: 创建 socket */
int sockfd = socket(AF_INET, SOCK_STREAM, );
if(sockfd < ){
perror("socket error");
exit();
} /** 往 serveraddr 中填入 ip、port 和地址族类型(ipv4) */
struct sockaddr_in serveraddr;
memset(&serveraddr, , sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[]));
/** 将 ip 地址转换成网络字节序后填入 serveraddr 中 */
inet_pton(AF_INET, argv[], &serveraddr.sin_addr.s_addr); /**
* 步骤2: 客户端调用 connect 函数连接到服务器端
*/
if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)) < ){
perror("connect error");
exit();
} /** 步骤3: 调用 IO 函数(read/write)和服务器端进行双向通信 */
char buffer[];
memset(buffer, , sizeof(buffer));
ssize_t size;
if((size = read(sockfd, buffer, sizeof(buffer))) < ){
perror("read error");
}
if(write(STDIN_FILENO, buffer, size) != size){
perror("write error");
} /** 步骤4: 关闭 socket */
close(sockfd); return ;
}
编译在两个终端上,一个打开服务器,一个打开客户端测试:

五十四、linux 编程——TCP 编程模型的更多相关文章
- 20190827 On Java8 第十四章 流式编程
第十四章 流式编程 流的一个核心好处是,它使得程序更加短小并且更易理解.当 Lambda 表达式和方法引用(method references)和流一起使用的时候会让人感觉自成一体.流使得 Java ...
- 孤荷凌寒自学python第五十四天使用python来删除Firebase数据库中的文档
孤荷凌寒自学python第五十四天使用python来删除Firebase数据库中的文档 (完整学习过程屏幕记录视频地址在文末) 今天继续研究Firebase数据库,利用google免费提供的这个数据库 ...
- 第三百五十四节,Python分布式爬虫打造搜索引擎Scrapy精讲—数据收集(Stats Collection)
第三百五十四节,Python分布式爬虫打造搜索引擎Scrapy精讲—数据收集(Stats Collection) Scrapy提供了方便的收集数据的机制.数据以key/value方式存储,值大多是计数 ...
- “全栈2019”Java第五十四章:多态详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Socket网络编程-TCP编程
Socket网络编程-TCP编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.socket介绍 1>.TCP/IP协议 2>.跨网络的主机间通讯 在建立通信连接的 ...
- 36 - 网络编程-TCP编程
目录 1 概述 2 TCP/IP协议基础 3 TCP编程 3.1 通信流程 3.2 构建服务端 3.3 构建客户端 3.4 常用方法 3.4.1 makefile方法 3.5 socket交互 3.4 ...
- abp(net core)+easyui+efcore实现仓储管理系统——出库管理之五(五十四)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...
- 五十四 网络编程 TCP编程
Socket是网络编程的一个抽象概念.通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可. 客户端 大多数连接都是可靠 ...
- 五十三、linux 编程——TCP 编程基本介绍
53.1 socket 套接字 53.1.1 介绍 Socket(套接字)是一种通讯机制,它包含一整套的调用接口和数据结构的定义,它给应用进程提供了使用如 TCP/UDP 灯网络协议进行网络通讯的手段 ...
随机推荐
- VS code 中的各种变量 ${file},${fileBasename}
VS code 中的各种变量 ${file},${fileBasename} 2017年08月24日 11:14:07 bailsong 阅读数:7108 from: https://blog. ...
- SQL Server系统表sysobjects介绍
SQL Server系统表sysobjects介绍 sysobjects 表结构: 列名 数据类型 描述 name sysname 对象名,常用列 id int 对象标识号 xtype char(2) ...
- SQL高级查询基础
1.UNION,EXCEPT,INTERSECT运算符 A,UNION 运算符 UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表. ...
- Linux(三)——Unix&Linux 的基础命令
Linux(三)--Unix&Linux 的基础命令 快捷键 Ctl-A 光标移动到行首 Ctl-C 终止命令 Ctl-D 注销登录 Ctl-E 光标移动到行尾 Ctl-U 删除光标到行首的所 ...
- VS Code常用快捷键大全
常用 General 按 Press 功能 Function Ctrl + Shift + P,F1 显示命令面板 Show Command Palette Ctrl + P 快速打开 Quick O ...
- requests库下载图片的方法
方法: 传入图片url,requests.get()方法请求一下,将源码以二进制的形式写在本地即可. 以前一直以为requests库中有特定的方法获取图片,类似urllib.request.urlre ...
- Webstorm 2018 激活破解
本文最后更新于 2018-5-4 可能会因为没有更新而失效.如已失效或需要修正,请留言! 问题 激活 webstorm 2018 最新版 解决步骤 License server:http://hb5. ...
- Java代理模式之Cglib代理
Cglib代理,也叫做子类代理.在内存中构建一个子类对象从而实现对目标对象功能的扩展. CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.不鼓励直接使用ASM,因 ...
- CentOS_7下安装Nginx服务
安装make: yum -y install gcc automake autoconf libtool make make是一个命令工具,是一个解释makefile中指令的命令工具.它可以简化编译过 ...
- Jetson TX1 install Opencv3
https://jkjung-avt.github.io/opencv3-on-tx2/ 注意:在编译的时候会遇到内存空间不足的情况,可以插入U盘,将程序拷贝到U盘内编译,然后安装到Jetson上.U ...