现在的位置: 首页 > 综合 > 正文

如 何 应 用TCP/IP 的 套 接 字 开 发 网 络 通 信 应 用 程 序

2013年09月09日 ⁄ 综合 ⁄ 共 6554字 ⁄ 字号 评论关闭

   最近正在学习java的网络编程,觉得网络编程很有趣,于是在网上找一些相关的资料,下面是我对套接子的认识.,

进 入 九 十 年 代 后, 随 着计 算 机 和 网 络 技 术 的 发 展, 很 多
数 据 处 理 系 统 都 采 用开 放 系 统 结 构 的 客 户 机/ 服 务 器 网 络
模 式, 即 客 户 机提 出 任 务 请 求, 通 过 网 络 发 送 给 服 务 器,
由 服 务 器 做相 应 处 理, 执 行 被 请 求 的 任 务, 然 后 将 结 果 返 回
给 客户 机。 例 如: 银 行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 !!
"); 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 !!
"); return(-1); } if ( listen(sockfd, 5) < 0 )/*建立长度为5的监听队列从套接字上收听连接请求*/ { fprintf(stderr, "Listen failed !!
"); return(-1); } fprintf(stderr, "blocked here.....
"); clilen="sizeof(cli_addr);" /*阻塞至客户方有连接请求到来*/ newsockfd="accept(sockfd," (struct sockaddr *)&cli_addr, &clilen); /*有连接请求时, 返回被赋予客户机地址的新套接字, 连接已建立*/ fprintf(stderr, "connected now!!!
"); if ( newsockfd < 0 ) { fprintf(stderr, "Accept failed!!
"); 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 !!
"); if (!memcmp(info,"123456",6)) {/*返回帐号为123456的帐户信息*/ printf("received account no is %6.6s
",info); sprintf(info,"%6.6s|%8.8s|%10.2lf",info,"zhang li",123.456); } else {/*否则, 返回000000表示无此帐户*/ printf("no such account no %6.6s
",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 !!
"); 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
");
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
"); 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 !!
");
/*客户进程向服务进程发送帐号信息*/
infolen=sizeof(info);
if ( (rc=TCPRECEIVE(sockfd, info, infolen)) == -1 )

fprintf(stderr, "TCPRecv failed !!
");
/*客户进程接收服务进程返回的帐户信息*/

sscanf(info,"%[^|]|%[^|]|%lf",data.accno,data.name,&data.amt); /*格式转换*/

if (!memcmp(data.accno,"000000",6))/*判断是否查到*/
printf("There is no such account in server database.
");
else {/*显示帐户信息*/
printf("accno is %s
",data.accno);
printf("name is %s
",data.name);
printf("amount is %-10.2lf
",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);
}

抱歉!评论已关闭.