五十四、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 灯网络协议进行网络通讯的手段 ...
随机推荐
- redis的穿透和雪崩
穿透: 从缓存中查询一个数据,查到为空,需要每次都去数据库中查询.而从数据库中查询出来也为空,也就不写入缓存.导致一个不存在的数每次都去数据库中查询,造成db系统很大压力 造成缓存穿透 解决:如果从数 ...
- SQL Server中将多行数据拼接为一行数据(一个字符串)
表A中id与表B中aid为一对多的关系 例如: 表A: id name a1 tom a2 lily a3 lucy 表B: id aid value b1 a1 B1 b2 a1 B2 b3 a2 ...
- c/c++ 右值引用,forward关键字
c++ forward关键字 forward的由来:模板函数中的推导类型,作为另一函数的参数时,不管实参是什么类型,作为另一个参数的实参时,都变成了左值.因为C++里规定函数的形参就是左值,不过调用侧 ...
- Linux、CentOS7下报错-bash: TMOUT: readonly variable怎么办?
一.Linux操作系统版本 二.背景:在项目中当我们配置好JDK环境变量.Tomcat环境变量,通过source /etc/profile使环境变量生效时,发现会报错,如图 三.解决 个人尚不知出现原 ...
- win7系统搭建FTP服务器
工作需要,所以研究了一下. 1. 打开: 控制面板 -> 卸载程序 -> (左侧)打开或关闭windows功能 等个一小会,勾选如下图红色方框内的选项. 2. 开始 -> 搜索: I ...
- LeetCode算法题-Self Dividing Numbers(Java实现)
这是悦乐书的第305次更新,第324篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第173题(顺位题号是728).自分割数是一个可被其包含的每个数字整除的数字.例如,12 ...
- js操作文章、字符串换行
操作前: 操作后: 第一步: 把中英文的逗号和顿号置换为 '\n’ support_unit = support_unit.replace(/,|,|./g, '\n') 第二步: //为了使\n ...
- springboot 2.1.4 源码默认logback-spring.xml
logback-spring.xml 是由几个文件组成的,整个的一个xml为 <?xml version="1.0" encoding="UTF-8"?& ...
- ProxyChains 的坑, 需要关闭 sip
之前 10.12.6 还可以的,现在升级下系统就用不了了. search下发生时sip问题. 解决方案就时要关闭这个东西; 关闭SIP reboot # 按住 option 键 # 到系统选择页面后, ...
- css display和vertical-align 属性
display 定义和用法 display 属性规定元素应该生成的框的类型. 实例 <html> <head> <style type="text/css&qu ...