unp第一章的測試程式碼
阿新 • • 發佈:2019-02-09
下面是很有趣的一個測試,下面是客戶端的測試程式:
/*daytimetcpcli2.c*/
#include "unp.h"
int
main(int argc, char **argv)
{
int sockfd, n, counter = 0;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: a.out <IPaddress>");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8088); /*這裡僅僅是埠號變化了 */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s" , argv[1]);
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
counter++;
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF)
err_sys("fputs error" );
}
if (n < 0)
err_sys("read error");
printf("counter = %d\n", counter);
exit(0);
}
然後是伺服器端的測試程式:
/*daytimesrv2.c*/
#include "unp.h"
#include <time.h>
int
main(int argc, char **argv)
{
int listenfd, connfd, i;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(8088);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for ( ; ; ) {
connfd = Accept(listenfd, (SA *) NULL, NULL);
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
for (i = 0; i < strlen(buff); i++)
Write(connfd, &buff[i], 1); //分多次寫
Close(connfd);
}
}
從上面我們可以看到,伺服器寫資料的時候多次寫,然而我們看一看在客戶端執行的結果,很有趣:
我們是一次接收到的,這說明tcp存在一個緩衝區,並不是每一次的寫都會導致資料傳送,為了提高運輸的效率,寫的資料會儲存起來,滿了之後一次性發送。
接下來是有關於客戶端和服務端的一些資訊的輸出,首先是客戶端:
/*daytimetcpcli3.c*/
#include "unp.h"
int
main(int argc, char **argv)
{
int sockfd, n;
socklen_t len;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr, cliaddr;
if (argc != 2)
err_quit("usage: a.out <IPaddress>");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9999); /* daytime server */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
len = sizeof(cliaddr);
Getsockname(sockfd, (SA *) &cliaddr, &len);//得到sock的名字
printf("local addr: %s\n",
Sock_ntop((SA *) &cliaddr, sizeof(cliaddr)));//輸出關於伺服器的一些訊息
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF)
err_sys("fputs error");
}
if (n < 0)
err_sys("read error");
exit(0);
}
然後是關於伺服器的程式碼:
/*daytimetcpsrv3.c*/
#include "unp.h"
#include <time.h>
int
main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t len;
struct sockaddr_in servaddr, cliaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999); /* daytime server */
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for ( ; ; ) {
len = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &len);
printf("connection from %s, port %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));//輸出客戶端的資訊
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff));
Close(connfd);
}
}
客戶端的執行情況:
服務端的執行狀況:
關於unp.h,由於該檔案太大,因此這裡只展示這裡需要的一小部分內容:
#ifndef __unp_h
#define __unp_h
#include <sys/types.h> /* basic system data types */
#include <sys/socket.h> /* basic socket definitions */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <errno.h>
#include <fcntl.h> /* for nonblocking */
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
/* Define bzero() as a macro if it's not in standard C library. */
#ifndef HAVE_BZERO
#define bzero(ptr,n) memset(ptr, 0, n)
/* $$.If bzero$$ */
/* $$.If memset$$ */
#endif
/* Following could be derived from SOMAXCONN in <sys/socket.h>, but many
kernels still #define it as 5, while actually supporting many more */
#define LISTENQ 1024 /* 2nd argument to listen() */
#define MAXLINE 4096 /* max text line length */
/* Following shortens all the typecasts of pointer arguments: */
#define SA struct sockaddr
void Write(int, void *, size_t);
int Accept(int, SA *, socklen_t *);
void Bind(int, const SA *, socklen_t);
void Connect(int, const SA *, socklen_t);
void Listen(int, int);
ssize_t Read(int fd, void *ptr, size_t nbytes);
#endif
下面是用到的一些包裹函式,之所以使用包裹函式,是為了減少一個函式體內的程式碼量,包裹了之後可以在一個函式體內減少很多的出錯處理,使程式碼變得簡單和優雅。
/* include Listen */
void
Listen(int fd, int backlog)
{
char *ptr;
/*4can override 2nd argument with environment variable */
if ( (ptr = getenv("LISTENQ")) != NULL)
backlog = atoi(ptr);
if (listen(fd, backlog) < 0)
err_sys("listen error");
}
ssize_t
Recv(int fd, void *ptr, size_t nbytes, int flags)
{
ssize_t n;
if ( (n = recv(fd, ptr, nbytes, flags)) < 0)
err_sys("recv error");
return(n);
}
int
Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
int n;
again:
if ( (n = accept(fd, sa, salenptr)) < 0) {
#ifdef EPROTO
if (errno == EPROTO || errno == ECONNABORTED)
#else
if (errno == ECONNABORTED)
#endif
goto again;
else
err_sys("accept error");
}
return(n);
}
void
Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (bind(fd, sa, salen) < 0)
err_sys("bind error");
}
void
Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (connect(fd, sa, salen) < 0)
err_sys("connect error");
}
ssize_t
Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ( (n = read(fd, ptr, nbytes)) == -1)
err_sys("read error");
return(n);
}
void
Write(int fd, void *ptr, size_t nbytes)
{
if (write(fd, ptr, nbytes) != nbytes)
err_sys("write error");
}
程式碼量有點大,先到這裡吧!