扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
进 入 九 十 年 代 后, 随 着计 算 机 和 网 络 技 术 的 发 展, 很 多 数 据 处 理 系 统 都 采 用开 放 系 统 结 构 的 客 户 机/ 服 务 器 网 络 模 式, 即 客 户 机提 出 任 务 请 求, 通 过 网 络 发 送 给 服 务 器, 由 服 务 器 做相 应 处 理, 执 行 被 请 求 的 任 务, 然 后 将 结 果 返 回 给 客户 机。 例 如: 银 行ATM 的 前 置 机 和 数 据 处 理 的 主 机 之 间即 构 成 客 户 机/ 服 务 器 网 络 模 式; 电 话 银 行 的 前 置 机和 银 行 数 据 处 理 机 之 间 也 构 成 这 种 网 络 模 式 结 构 等。这 样, 如 何 在 前 置 机 和 数 据 主 机 之 间 进 行 信 息 交 换,即 进 程 网 络 通 信, 就 成 为 实 现 这 种 网 络 模 式 的 基 础。而TCP/IP 的 套 接 字 技 术 是 解 这 一 问 题 的 有 力 工 具。 它 从提 出 时 就 一 直 发 挥 着 愈 来 愈 重 要 的 作 用, 并 已 成 为UNIX 操 作 系 统 下TCP/IP 网 络 编 程 标 准; 甚 至WINDOW、JAVA 都 配 有 它的 通 用 接 口。 有 了 这 个 强 有 力 的 工 具, 我 们 可 以 实 现异 种 机、 异 种 操 作 系 统 应 用 程 序 间 的 相 互 连 接 和 通信。
套 接 字(sockets) 是 支 持TCP/IP 协 议 的 网 络 通 信 的 基 本 操 作 单 元。 可 以 将 套 接 字 看 作不 同 主 机 间 的 进 程 进 行 双 向 通 信 的 端 点。 它 构 成 了 在单 个 主 机 内 及 整 个 网 际 间 的 编 程 界 面。 一 般 来 说, 跨机 应 用 进 程 之 间 要 在 网 络 环 境 下 进 行 通 信, 必 须 要 在网 络 的 每 一 端 都 要 建 立 一 个 套 接 字, 两 个 套 接 字 之 间是 可 以 建 立 连 接 的, 也 是 可 以 无 连 接 的, 并 通 过 对 套接 字 的“ 读”、“ 写” 操 作 实 现 网 络 通 信 功 能。 类 似 于UNIX 系 统 中 的I/O 概 念, 像 文 件 那 样 有 打 开、 读、 写、 关 闭 的方 式。 根 据 传 输 数 据 类 型 的 不 同, 套 按 字 可 分 为 面 向连 接 的 数 据 套 接 字(stream sockets) 和 无 连 接 的 数 据 报 套接 字(datagram sockets) 两 种 类 型。
1、 字 节 流 套 接 字
字 节 流 不 按 记 录 定 界,在TCP/IP 协 议 簇 中 对 应TCP 协 议, 即 传 输 控 制 协 议(Transmition Control Protocol)。 它 是 一 个 提 供 给 用 户 进 程 可 靠 的 全 双 工的 面 向 连 接 的 协 议, 大 多 数INTERNET 应 用 程 序 如ftp、telnet 使用TCP 协 议。 通 信 端 点 使 用TCP 对 应 的INTERNET 地 址 互 相 连接, 可 保 证 按 正 确 的 顺 序 以 及 单 一 和 可 靠 的 地 址 传 输数 据。 由 于 它 是 字 节 流, 所 以 包 长 包 没 有 限 制, 信 包传 输 也 不 重 复, 因 而 是 一 种 常 用 的 套 接 字 类 型。
2、 数 据 报 套 接 字
数 据 报 对 应 记 录 型 数 据流, 在TCP/IP 协 议 簇 中 对 应UDP 协 议, 即 用 户 数 据 报 协 议(User Datagram Protocol)。 利 用 数 据 报 服 务 可 实 现 一 些 简 单 的 网络 服 务, 如 网 点 检 测 程 序PING。 由 于 不 建 立 连 接, 数 据报 协 议 比 连 接 协 议 快。 但 不 能 保 证 所 有 数 据 都 准 确 有序 地 到 达 目 的 地。 不 保 证 顺 序 性、 可 靠 性 和 无 重 复性。 它 是 无 连 接 的 服 务, 以 独 立 的 信 包 进 行 传 输, 通信 端 点 使 用UDP 对 应 的INTERNET 地 址。 双 方 不 需 互 连, 按 固定 的 最 大 长 度 进 行 传 输, 因 而 适 用 于 单 个 报 文 传 输,或 较 小 文 件 的 传 输。
套 接 字 的 编 程 要 点 及 和 过 程
不 论 何 种 套 接 字 编 程,均 采 用 客 户 机/ 服 务 器 方 式, 其 运 作 过 程 基 本 类 似, 限于 篇 幅, 这 里 仅 介 绍 字 节 流 套 接 字。 字 节 流 套 按 字 的服 务 进 程 和 客 户 进 程, 在 通 信 前 必 须 创 建 各 自 的 套 接字 以 建 立 连 接, 然 后 对 相 应 的 套 接 字 进 行“ 读”、“ 写” 操 作, 实 现 信 息 的 交 换。
服 务 器 进 程 创 建 套 接 字。 服 务 进 程 总 是 先 于 客 户 进程 启 动, 服 务 进 程 首 先 调 用socket() 函 数 创 建 自 已 端 的 一个 字 节 流 套 接 字, 并 提 供 三 个 参 数: 网 络 地 址 类 型,一 般 取AF_INEF(Adress family InterNET); 套 接 字 类 型, 这 里 取SOCK_STREAM;网 络 协 议, 缺 省 为TCP/IP 协 议, 对 应 参 数 为0。
给 套 接 字 地 址 变 量 赋 初 值。 在 生 成 套 接 字 后, 要 用服 务 器 的 地 址 先 对sockaddr_in 结 构 变 量 赋 初 值。Sockaddr_in 在/usr/include/netinet/in.h 中 有 定 义, 它 只 适 用 于INTERNET 地 址 类 型, 含 有INTERNET 套 接字 地 址 类 型、IP 端 口 号、IP 地 址 等 信 息。 地 址 类 型 可 取定 为AF_INET,IP 地 址 对 服 务 器 可 取 任 意 合 法 地 址INADDR_ANY。IP 端 口 号 可 由 用 户 设 定, 但 要 注 意 主 机 字 节 顺 序 向 网 络字 节 顺 序 的 转 换。
给 套 接 字 命 名。 由socket() 函 数 创 建 的 套 接 字 是 没 有 名字 的。 所 谓 命 名, 就 是 用bind() 函 数 将 服 务 器 地 址 捆 绑到 创 建 的 套 接 字 上。
服 务 器 进 程 准 备 接 受 来 自 客 户 机 的 连 接 请 求。 首 先调 用listen() 函 数, 让 服 务 器 进 程 进 入 监 听 状 态; 然 后 调用accept() 函 数, 准 备 接 受 客 户 机 的 连 接 信 号。 无 连 接 请求 时, 服 务 进 程 被 阻 塞。
客 户 进 程 调 用socket() 函 数 创 建 已 端 的 套 接 字。
给 客 户 端 的sockaddr_in 结 构 体 变 量 赋 值。 地 址 类 型 仍 可取AF_INET, 端 口 号 和 服 务 器 方 的 端 口 号 相 同, 欲 连 服 务器 的 地 址 通 过 调 用inet_addr() 转 换 得 到。 也 可 通 过gethostbyname() 函 数 将 名 字 转 换 为 指 向hostent 结 构 变 量 的 指 针, 再 将hostent 结 构 变 量 的 地 址 成 员 用bcopy() 复 制 到sockaddr_in 结 构 变 量上。
客 户 方 调 用connect() 函 数 向 服 务 进 程 发 出 连 接 请 求。
当 连 接 请 求 到 来 后, 被 阻 塞 服 务 进 程 的accpet() 函 数 生成 一 个 新 的 字 节 流 套 接 字, 并 返 回 客 户 机 的sockaddr_in 结构 变 量, 从 而 在 服 务 器 应 用 程 序 中 用 新 的 被 赋 予 客 户机 地 址 的 套 接 字 同 客 户 进 程 进 行 连 接, 然 后 向 客 户 方返 回 接 受 信 号。
一 旦 客 户 机 的 套 接 字 收 到 来 自 服 务 器 的 接 受 信 号,则 表 示 客 户 机 与 服 务 器 双 方 已 实 现 连 接。 任 一 方 均 可向 对 方 发 送, 也 可 接 收 对 方 发 来 的 数 据。 这 既 可 通 过send()、recv() 函 数 来 实 现。 也 可 通 过read()、write() 函 数 来 交 换 数 据。
服 务 进 程 和 客 户 进 程 可 通 过 调 用shutdown() 和colse() 关 闭 套接 字 上 的 所 有 发 送 和 接 收 操 作, 撤 销 套 接 字 并 中 断 连接。
上 述 所 有 系 统 函 数 均包 括 在libsocket.a 系 统 库 中,IBM PC(UNIX) 操 作 系 统 编 译 时 需加-llibsocket 链 接。IBM RS/6000(AIX) 操 作 系 统 统 编 译 时 无 需 此 参数。
编 程 示 例
本 文 给 出 一 个 运 用 字节 流 套 接 字, 在TCP/IP 网 络 上 实 现 客 户 机/ 服 务 器 方 式 进程 通 信 的 实 例, 该 程 序 在IBM RS/6000 小 型 机 和IBM PC 586 微 机上 调 试 通 过。 它 主 要 模 拟 根 据 帐 号 查 询 余 额 的 过 程,客 户 机 是 一 台 电 话 银 行 的 前 置 机(IBM PC), 服 务 器 是 银行 数 据 处 理 主 机(IBM RS/6000), 包 括 三 方 面:
客 户 机 从 标 准 输 入 读 入 帐 号, 并 将 该 帐 号 发 送 给 服务 器。
服 务 器 接 收 该 帐 号 后, 进 行 判 断 是 否 有 此 帐 号 的 数据, 并 将 结 果 返 回 给 客 户 机。
客 户 机 接 收 返 回 的 信 息, 并 将 结 果 输 出 在 标 准 输 出上。
服 务 进 程 需 先 于 客 户进 程 启 动。 客 户 进 程 启 动 时 要 携 带 服 务 器 的IP 地 址,如:clientpros 23.169.1.1
在UNIX 操 作 系 统 下 可 用Doscp 命 名 拷 入 硬 盘; 编 译 时 用:
cc hxserver.c -lsocket -0 server
cc hxclient.c -lsocket -0 client
在AIX 操 作 系 统 下 可 用Dosread 命 名 拷 入 硬 盘, 编 译 时 用:
cc hxserver.c -0 server
cc hxclient.c -0 client
/*服务器端程序:hxserver.c*/
#include
#include
#include
#include
#include "tcpipop.c"
main()
{
int newsockfd, sockfd;
int clilen;
struct sockaddr_in cli_addr, serv_addr;
char info[100];
int infolen;
int rc;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) <0)/*创建TCP协议的字节流*/ { fprintf(stderr, "Socket failed !!\n"); return(-1); } bzero((char *)&serv_addr, sizeof(serv_addr));/*服务器地址清0*/ serv_addr.sin_family="AF_INET;/*网络类型使用ARPA" internet地址*/ serv_addr.sin_addr.s_addr="htonl(INADDR_ANY);" /*IP地址取公认的任意合法地址*/ serv_addr.sin_port="htons(1234);" /*设定IP端口号, 并将主机字节顺序转换为网络字节顺序*/ if ( bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0 ) /*将服务器地址信息捆绑到创建的套接字上*/ { fprintf(stderr, "Bind failed !!\n"); return(-1); } if ( listen(sockfd, 5) < 0 )/*建立长度为5的监听队列从套接字上收听连接请求*/ { fprintf(stderr, "Listen failed !!\n"); return(-1); } fprintf(stderr, "blocked here.....\n"); clilen="sizeof(cli_addr);" /*阻塞至客户方有连接请求到来*/ newsockfd="accept(sockfd," (struct sockaddr *)&cli_addr, &clilen); /*有连接请求时, 返回被赋予客户机地址的新套接字, 连接已建立*/ fprintf(stderr, "connected now!!!\n"); if ( newsockfd < 0 ) { fprintf(stderr, "Accept failed!!\n"); return(-1); } shutdown(sockfd, 2); close(sockfd); memset(info,0,sizeof(info)); infolen="6;" if ((rc="TCPRECEIVE(newsockfd," info, infolen))="=" 1)/*接受客户机发来的信息*/ fprintf(stderr, "TCPRecv failed !!\n"); if (!memcmp(info,"123456",6)) {/*返回帐号为123456的帐户信息*/ printf("received account no is %6.6s\n",info); sprintf(info,"%6.6s|%8.8s|%10.2lf",info,"zhang li",123.456); } else {/*否则, 返回000000表示无此帐户*/ printf("no such account no %6.6s\n",info); sprintf(info,"%6.6s|%8.8s|%lf","000000","",0.0); } infolen="sizeof(info);" if ((rc="TCPSEND(newsockfd," info, infolen) )="=" 1) /*向客户机发送该帐户信息*/ fprintf(stderr, "TCPSend failed !!\n"); shutdown(newsockfd, 2); close(newsockfd); } /*客户端程序:hxclient.c*/ #include
#include
#include
#include
#include "tcpipop.c"
typedef struct{
char accno[7];
char name[9];
double amt;
} data_type;
/* 帐户数据结构 */
main(argc,argv)
int argc;
char ** argv;
{
int I=0;
int sockfd;
struct sockaddr_in serv_addr;/*存服务器方的地址*/
char info[100];
int infolen;
data_type data;
int rc;
if (argc!=2)
{
fprintf(stderr,"client ipaddress\n");
exit(-1);
}
/*参数是服务器的IP地址*/
for ( ; I<10; I++ )/*最多连接10次*/ { bzero((char *)&serv_addr, sizeof(serv_addr)); /*服务器地址变量清0*/ serv_addr.sin_family="AF_INET;" /*网络地址类型使用ARPA internet 地址*/ serv_addr.sin_addr.s_addr="inet_addr(argv[1]);" /*服务器IP地址转换为长整型IP地址, 并赋予地址结构变量*/ serv_addr.sin_port="htons(1234);" /*与服务器相同的IP端口号, 经主机字节顺序到网络字节顺序的转换*/ if ((sockfd="socket(AF_INET," SOCK_STREAM, 0)) < 0) /*创建TCP协议的字节流套接字*/ { fprintf(stderr, "Client : Can not open stream socket\n"); shutdown(sockfd, 2); close(sockfd); return(-1); } if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))>= 0)
/*与服务进程建立连接*/
break;
shutdown(sockfd, 2);
close(sockfd);
}
if ( I >= 10 )
exit(-1);
/*此时连接已建立, 可通过对套接字的读写实现通信*/
memset(&data,0,sizeof(data));
printf("enter account no:");
scanf("%s",data.accno); /*输入要检索的帐号*/
infolen=sizeof(data.accno);
bcopy(&data.accno,info,infolen);
if ( (rc=TCPSEND(sockfd, info, infolen)) == -1 )
fprintf(stderr, "TCPSend failed !!\n");
/*客户进程向服务进程发送帐号信息*/
infolen=sizeof(info);
if ( (rc=TCPRECEIVE(sockfd, info, infolen)) == -1 )
fprintf(stderr, "TCPRecv failed !!\n");
/*客户进程接收服务进程返回的帐户信息*/
sscanf(info,"%[^|]|%[^|]|%lf",data.accno,data.name,&data.amt); /*格式转换*/
if (!memcmp(data.accno,"000000",6))/*判断是否查到*/
printf("There is no such account in server database.\n");
else {/*显示帐户信息*/
printf("accno is %s\n",data.accno);
printf("name is %s\n",data.name);
printf("amount is %-10.2lf\n",data.amt);
}
shutdown(sockfd, 2);
close(sockfd);
}
/*公共程序:tcpipop.c*/
/*
* Using TCP/IP Protocol.
*/
#include
#include
#include
#include
#include
extern int TCPSEND(int fd, char *buf, int len)
{
int I=0;
int send_rc;
for ( ; I<10; I++ ) { send_rc="write(" fd, buf, len ); if ( send_rc="=" len ) break; sleep(1); } if ( I>= 10 )
return(-1);
else
return(send_rc);
}
extern int TCPRECEIVE(int fd, char *buf, int len)
{
int I=0;
int receive_rc;
for ( ; I<10; I++ ) { receive_rc="read(fd," buf, len); if ( receive_rc="=" len ) break; sleep(1); } if ( I>= 10 )
return(-1);
else
return(receive_rc);
}
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者