Unix/Linux编程:客户应用程序------DAYTIME、TIME、ECHO
作者:互联网
准备
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif /* INADDR_NONE */
extern int errno;
int errexit(const char *format, ...);
/* ------------------------------------------------------------------------
* connectsock - allocate & connect a socket using TCP or UDP
*------------------------------------------------------------------------
* Arguments:
* host - name of host to which connection is desired
* service - service associated with the desired port
* transport - name of transport protocol to use ("tcp" or "udp")
*/
int connectsock(const char *host, const char *service, const char *transport ){
struct hostent *phe; /* pointer to host information entry */
struct servent *pse; /* pointer to service information entry */
struct protoent *ppe; /* pointer to protocol information entry*/
struct sockaddr_in sin; /* an Internet endpoint address */
int s, type; /* socket descriptor and socket type */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
/* Map service name to port number */
if ( pse = getservbyname(service, transport) )
sin.sin_port = pse->s_port;
else if ((sin.sin_port=htons((unsigned short)atoi(service))) == 0)
errexit("can't get \"%s\" service entry\n", service);
/* Map host name to IP address, allowing for dotted decimal */
if ( phe = gethostbyname(host) )
memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )
errexit("can't get \"%s\" host entry\n", host);
/* Map transport protocol name to protocol number */
if ( (ppe = getprotobyname(transport)) == 0)
errexit("can't get \"%s\" protocol entry\n", transport);
/* Use protocol to choose a socket type */
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
/* Allocate a socket */
s = socket(PF_INET, type, ppe->p_proto);
if (s < 0)
errexit("can't create socket: %s\n", strerror(errno));
/* Connect the socket */
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
errexit("can't connect to %s.%s: %s\n", host, service,
strerror(errno));
return s;
}
/*------------------------------------------------------------------------
* connectTCP - connect to a specified TCP service on a specified host
*------------------------------------------------------------------------
* Arguments:
* host - name of host to which connection is desired
* service - service associated with the desired port
*/
int connectTCP(const char *host, const char *service )
{
return connectsock( host, service, "tcp");
}
/*------------------------------------------------------------------------
* connectUDP - connect to a specified UDP service on a specified host
*------------------------------------------------------------------------
* Arguments:
* host - name of host to which connection is desired
* service - service associated with the desired port
*/
int connectUDP(const char *host, const char *service )
{
return connectsock(host, service, "udp");
}
/*------------------------------------------------------------------------
* errexit - print an error message and exit
*------------------------------------------------------------------------
*/
int errexit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}
DAYTIME服务
用于获得当前时间并按照人可读的格式打印
TCP/IP标准定义了一个应用协议,该协议允许用户获得当前的日期和时间,输出格式采用用户能读懂的形式。该服务正式命名为DAYTIME服务
为访问DAYTIME服务,用户需要调用客户应用程序。这个客户联系服务器以获得信息,并将该信息打印出来。尽管标准没有指明精确的语法,但它建议了一些格式。比如DAYTIME可以按照如下形式提供日期和时间:
w
e
e
k
d
a
y
,
m
o
n
t
h
,
d
a
y
,
y
e
a
r
,
t
i
m
e
−
t
i
m
e
z
o
n
e
weekday, month, day, year, time-timezone
weekday,month,day,year,time−timezone
T h u r s d a y , F e b r u a r y , 22 , 200112 : 37 : . 21 − P S T Thursday, February, 22, 2001 12:37:.21-PST Thursday,February,22,200112:37:.21−PST
该标准指明,DAYTIME服务既可以针对TCP也可以针对UDP。两者都运行在协议端口13上。
DAYTIME的TCP版本利用TCP连接的出现来激活输出:只要一个新的连接到达,服务器就构造包含当前日期和时间的文本字符串,发送这个字符串,然后将连接关闭。这样,客户根部不用发送任何请求。实际上,标准指明,服务器必须丢弃客户发送的任何数据。
DAYTIME的UDP版本要求客户发送请求。请求由任意UDP数据报构成。服务器只要收到数据报,它就格式化当前的如何和时间,将结果字符串放置到外发数据报中,然后将其发回客户。服务器一旦发送量应答,它就将激活这个响应的数据报丢弃
针对DAYTIME的TCP客户实现
// TCPdaytime
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int TCPdaytime(const char *host, const char *service);
int errexit(const char *format, ...);
int connectTCP(const char *host, const char *service);
#define LINELEN 128
/*------------------------------------------------------------------------
* main - TCP client for DAYTIME service
*------------------------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = "localhost"; /* host to use if none supplied */
char *service = "daytime"; /* default service port */
switch (argc) {
case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: TCPdaytime [host [port]]\n");
exit(1);
}
TCPdaytime(host, service);
exit(0);
}
/*------------------------------------------------------------------------
* TCPdaytime - invoke Daytime on specified host and print results
*------------------------------------------------------------------------
*/
int TCPdaytime(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
int s, n; /* socket, read count */
s = connectTCP(host, service);
while( (n = read(s, buf, LINELEN)) > 0) {
buf[n] = '\0'; /* ensure null-terminated */
(void) fputs( buf, stdout );
}
return 0;
}
连接一旦建立,DAYTIME仅仅从这个连接中读取输入并将其打印。它不断重复的读,直到检测出文件结束的条件
从TCP连接中读
这也说明了一个重要的思想:TCP提供了一种流服务,而不保证保持记录的边界。实际上,流模式意味着TCP使发送应用程序和接收应用程序分隔开了。比如,假设发送程序在一次send调用中发送了64个字节,接着第二次调用又发送了64字节。接收应用可能在对read的一次调用中就收到了所有这128各字节,或者它可能第一次调用时收到10字节,第二次调用收到100字节,第三次调用收到18字节。在一次调用中返回的字节数依赖于下层互联网数据报大小、可用的缓存空间以及穿越网络所遇到的时延。
因为TCP的流服务不能保证按写入时相同的数据块交付数据,从TCP连接接受数据的应用程序不能指望所有数据能够在一次传送中交付完;它必须重复的调用recv(或者read),直到获得了所有的数据
TIME服务
用于获得32bit整数形式的时间
TCP/IP定义了一个服务,它允许一台机器从另一条机器获得当前的日期和时间。该服务正式命名为TIME,而且非常简单:在一台机器中运行的某个客户程序向在另一条机器中执行的服务器发送请求。服务器只要收到请求,就从本地操作系统中获得当前的日期和时间,用标准的格式编码该信息,然后在响应中将它发送给客户。
客户和服务器可能出于不同的时区,为避免由此发生的问题,TIME协议指定,所有时间和日期都必须用标准国际时区UCT表示。这样,服务器在发送应答时,将本地时间转为国际时间,客户在应答到达时,将国际时间转为本地时间。
DAYTIME服务意在为人所用,而TIME服务意在为那些存储和维护时间的程序所使用,TIME协议用一个32bit的整数表明时间,它表示从格林尼治时间起所经历的秒数
访问TIME服务
客户可以使用TCP或UDP在协议端口37上访问TIME服务(在技术上,标准定义了两个单独的服务,一个针对UDP,一个针对TCP)。为TCP构建的TIME服务器利用连接的出现来激活输出。这与上面的TIME服务很像。客户构造到TIME服务器的链接,并等待读取链接的输出。当服务器检测到新的链接,就把当前时间作为一个整数发送出去,然后关闭链接。客户不发送任何数据,因为服务器不从链接中读数据
客户也可以使用UDP访问TIME服务,为此,客户发送仅包含单个数据报的请求。服务器并不处理这个传入的数据报,而只是从中取出发送者的地址和协议端口号,以便在应答中使用。服务器将当前时间编码为一个整数,将其放在数据报中,并将此数据报发回给客户,
精确时间和网络时延
尽管TIME服务适用于不同的时区,但它不能处理网络的时延。
但是,TIME服务仍旧很流行,原因有三。第一,与时钟同步协议比较,TIME是及其简单的;第二,大多数客户在同一个局域网中联系服务器,网络时延就只有几毫秒;第三,处理那些要利用时间戳来控制进程的程序外,人民不关心这个误差
在要求更高精确性的场合,改进TIME或者使用其他协议是有可能的。提高TIME的精确性的最简单途径是计算一下服务器到客户的网络时延近似值,然后将此近似值加到服务器所报告的时间值上。比如,计算网络时延近似值的一种方法是:客户计算从客户到服务器,再从服务器回来这一来一往时延。然后取往返时延的一半作为时延的近似值。客户将此时延的近似值加到服务器所返回的时间值上
针对TIME服务的UDP客户实现
// UDPTime.c
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#define BUFSIZE 64
#define UNIXEPOCH 2208988800UL /* UNIX epoch, in UCT secs */
#define MSG "what time is it?\n"
int connectUDP(const char *host, const char *service);
int errexit(const char *format, ...);
/*------------------------------------------------------------------------
* main - UDP client for TIME service that prints the resulting time
*------------------------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = "localhost"; /* host to use if none supplied */
char *service = "time"; /* default service name */
time_t now; /* 32-bit integer to hold time */
int s, n; /* socket descriptor, read count*/
switch (argc) {
case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: UDPtime [host [port]]\n");
exit(1);
}
s = connectUDP(host, service);
(void) write(s, MSG, strlen(MSG));
/* Read the time */
n = read(s, (char *)&now, sizeof(now));
if (n < 0)
errexit("read failed: %s\n", strerror(errno));
now = ntohl((unsigned long)now); /* 将now从网络字节序转为本地主机字节序 */
now -= UNIXEPOCH; /* convert UCT to UNIX epoch */
printf("%s", ctime(&now));
exit(0);
}
该例子通过发送数据报联系TIME服务。然后,调用read等待并从应答中取出时间值。
这个例子在Linux上运行,与Internet不同的是,Linux假设了起始日期为格林威治时间(1970-1-1)。因此,需要将TIME协议的起始点转换为Linux的起始点,客户必须减去1900-1-1到1970-1-1之间的秒数2208988800UL 。时间一旦转换成与本机兼容的表示之后,就可以调用ctime,将时间转换为人可读的形式输出。
ECHO
用于测试网络连通性
TCP/UDP服务为UDP和TCP都指明了一种ECHO服务。ECHO服务仅返回它从客户收到的所有数据。可以为网络管理员测试可达性,调试协议软件以及识别选路问题。
TCP ECHO服务器指明,服务器必须接收传入连接请求,从连接中读取数据,然后在该连接上讲数据写回,如此进行,直到客户终止传送。而与此同时,客户发送输入数据,然后读取返回的数据
针对ECHO服务的TCP客户
/* TCPecho.c - main, TCPecho */
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define LINELEN 128
int TCPecho(const char *host, const char *service);
/*------------------------------------------------------------------------
* main - TCP client for ECHO service
*------------------------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = "localhost"; /* host to use if none supplied */
char *service = "echo"; /* default service name */
switch (argc) {
case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: TCPecho [host [port]]\n");
exit(1);
}
TCPecho(host, service);
exit(0);
}
/*------------------------------------------------------------------------
* TCPecho - send input to ECHO service on specified host and print reply
*------------------------------------------------------------------------
*/
int TCPecho(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
int s, n; /* socket descriptor, read count*/
int outchars, inchars; /* characters sent and received */
s = connectTCP(host, service);
while (fgets(buf, sizeof(buf), stdin)) {
buf[LINELEN] = '\0'; /* insure line null-terminated */
outchars = strlen(buf);
(void) write(s, buf, outchars);
/* read it back */
for (inchars = 0; inchars < outchars; inchars+=n ) {
n = read(s, &buf[inchars], outchars - inchars);
if (n < 0)
errexit("socket read failed: %s\n",
strerror(errno));
}
fputs(buf, stdout);
}
}
在打开链接之后,TCPecho就进入了循环,该循环重复的读取每行输入,通过TCP链接将该行输入发送给ECHO服务器,再读取返回的数据并将其打印出来。在所有行都已经发送给服务器,接收到了返回的数据并将其打印出来之后,客户就退出
针对ECHO服务的UDP客户
/* UDPecho.c - main, UDPecho */
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define LINELEN 128
int UDPecho(const char *host, const char *service);
/*------------------------------------------------------------------------
* main - UDP client for ECHO service
*------------------------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = "localhost";
char *service = "echo";
switch (argc) {
case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: UDPecho [host [port]]\n");
exit(1);
}
UDPecho(host, service);
exit(0);
}
/*------------------------------------------------------------------------
* UDPecho - send input to ECHO service on specified host and print reply
*------------------------------------------------------------------------
*/
int UDPecho(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
int s, nchars; /* socket descriptor, read count*/
s = connectUDP(host, service);
while (fgets(buf, sizeof(buf), stdin)) {
buf[LINELEN] = '\0'; /* insure null-terminated */
nchars = strlen(buf);
(void) write(s, buf, nchars);
if (read(s, buf, nchars) < 0)
errexit("socket read failed: %s\n",
strerror(errno));
fputs(buf, stdout);
}
}
算法和TCP客户一样:重复的读取输入行,将其发送给服务器,然后在读取返回的这些数据,并将其打印出来。
UCP和TCP版本之间的最大区别在于它们如何处理从服务器收到的数据。因为UDP是面向数据报的,客户将一个输入行作为一个单元,将其放置在单独的数据报中。同样的,ECHO服务器接收并返回整个数据报。因此,TCP客户把传入的数据作为字节流读取,而UDP客户要么收到了由服务器返回的整个行,要么什么也没有收到;除非出现差错,否则每次调用read都返回整个行
标签:const,service,int,host,DAYTIME,char,Unix,TIME,include 来源: https://blog.csdn.net/zhizhengguan/article/details/117728935