标签:style blog http color 使用 io 文件 for ar
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie
下面我会介绍同一个使用 TCP 协议的客户端程序的几个不同版本,分别是停等版本、select 加阻塞式 I/O 版本、
非阻塞式 I/O 版本、fork 版本、线程化版本。它们都由同一个 main 函数调用来实现同一个功能,即回射程序客户端。#include "unp.h"
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: tcpcli <IPaddress>");
//1.创建 TCP 套接字
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
//2.指定服务器的 IP 地址和端口
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
//3.建立与服务器的连接
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
//4.str_cli 函数完成剩余部分的客户处理工作
str_cli(stdin, sockfd);
exit(0);
}
#include "unp.h"
void
str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE];
//1.从 fp 读入一行,存放到 sendline
while (Fgets(sendline, MAXLINE, fp) != NULL) {
//2.将 sendline 的内容写到 sockfd 连接的服务器
Writen(sockfd, sendline, strlen(sendline));
//3.从服务器读入回射行,存放到 recvline
if (Readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
//4.将 recvline 的内容写到标准输出
Fputs(recvline, stdout);
}
}/**
* TCP 使用 select
**/
#include "unp.h"
void
str_cli(FILE *fp, int sockfd)
{
int maxfdp1; // maxfdpl 参数指定待测定的描述符个数
fd_set rset; //可读描述符集合
char sendline[MAXLINE], recvline[MAXLINE];
//1.调用 select
FD_ZERO(&rset); //初始化 rset
for ( ; ; ) {
FD_SET(fileno(fp), &rset); // 打开标准I/O文件指针 fp 对应的位
FD_SET(sockfd, &rset); // 打开套接字 sockfd 对应的位
maxfdp1 = max(fileno(fp), sockfd) + 1;
//调用 select 阻塞到某个描述符就绪为止
Select(maxfdp1, &rset, NULL, NULL, NULL);
//2.处理可读套接字
if (FD_ISSET(sockfd, &rset)) {
//使用 readline 读入回射文本,再用 fputs 输出到 stdout
if (Readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
Fputs(recvline, stdout);
}
//3.处理可读输入
if (FD_ISSET(fileno(fp), &rset)) {
//使用 fgets 读入一行文本,再用 writen 把它写到套接字中
if (Fgets(sendline, MAXLINE, fp) == NULL)
return; /* all done */
Writen(sockfd, sendline, strlen(sendline));
}
}
}
/**
* TCP 使用 select 并操纵缓冲区
**/
#include "unp.h"
void
str_cli(FILE *fp, int sockfd)
{
//stdineof 表示标准输入是否结束
int maxfdp1, stdineof;
fd_set rset;
char buf[MAXLINE];
int n;
//1.调用 select
stdineof = 0;
FD_ZERO(&rset);
for ( ; ; ) {
if (stdineof == 0)
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
//2.处理可读套接字
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
if (stdineof == 1) //如果在套接字上读到 EOF,并且已在标准输入上遇到 EOF,那就是正常的终止
return;
else //否则,服务器进程过早终止了
err_quit("str_cli: server terminated prematurely");
}
Write(fileno(stdout), buf, n);
}
//3.处理可读输入
if (FD_ISSET(fileno(fp), &rset)) {
//如果在标准输入上碰到 EOF 时,把 stdineof 置为 1
//并调用 shutdown 函数向服务器发送 FIN,关闭客户端向服务器的写操作
if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) {
stdineof = 1;
Shutdown(sockfd, SHUT_WR);
FD_CLR(fileno(fp), &rset);
continue;
}
Writen(sockfd, buf, n);
}
}
}/* include nonb1 */
#include "unp.h"
void
str_cli(FILE *fp, int sockfd)
{
int maxfdp1, val, stdineof;
ssize_t n, nwritten;
fd_set rset, wset;
char to[MAXLINE], fr[MAXLINE];
//to 容纳从标准输入到服务器去的数据
//from 容纳自服务器到标准输出来的数据
char *toiptr, *tooptr, *friptr, *froptr;
//1.把描述符设置为非阻塞
val = Fcntl(sockfd, F_GETFL, 0);
Fcntl(sockfd, F_SETFL, val | O_NONBLOCK); //套接字描述符
val = Fcntl(STDIN_FILENO, F_GETFL, 0);
Fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK); //标准输入
val = Fcntl(STDOUT_FILENO, F_GETFL, 0);
Fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK); //标准输出
//2.初始化缓冲区指针
toiptr = tooptr = to;
friptr = froptr = fr;
stdineof = 0;
maxfdp1 = max(max(STDIN_FILENO, STDOUT_FILENO), sockfd) + 1;
//3.主循环:调用 select
//一个 select 调用后对所关注各个条件进行单独测试
for ( ; ; ) {
//指定所关注的描述符
FD_ZERO(&rset);
FD_ZERO(&wset);
if (stdineof == 0 && toiptr < &to[MAXLINE])
FD_SET(STDIN_FILENO, &rset); /* 打开读描述符集中对应标准输入的位 */
if (friptr < &fr[MAXLINE])
FD_SET(sockfd, &rset); /* 打开读描述符集中对应套接字的位 */
if (tooptr != toiptr)
FD_SET(sockfd, &wset); /* 打开写描述符集中对应套接字的位 */
if (froptr != friptr)
FD_SET(STDOUT_FILENO, &wset); /* 打开写描述符集中对应标准输出的位 */
//调用 select
Select(maxfdp1, &rset, &wset, NULL, NULL);
/* end nonb1 */
/* include nonb2 */
//4.对所关注各个条件进行单独测试
//从标准输入 read
if (FD_ISSET(STDIN_FILENO, &rset)) {
//read 返回错误。 EWOULDBLOCK 是正常的错误,表明这个描述符还处在阻塞中,应该忽略该错误
if ( (n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0) {
if (errno != EWOULDBLOCK)
err_sys("read error on stdin");
}
//read 返回 EOF,标准输入处理结束
else if (n == 0) {
#ifdef VOL2
fprintf(stderr, "%s: EOF on stdin\n", gf_time());
#endif
stdineof = 1; /* 标志标准输入结束 */
if (tooptr == toiptr) // to 缓冲区中已经没有数据要发送
Shutdown(sockfd, SHUT_WR);/* 关闭客户端向服务器的写操作 */
}
//read 返回数据
else {
#ifdef VOL2
fprintf(stderr, "%s: read %d bytes from stdin\n", gf_time(), n);
#endif
toiptr += n; //增加 toiptr
FD_SET(sockfd, &wset); //打开写描述符集中对应套接字的位
}
}
//从套接字 read
if (FD_ISSET(sockfd, &rset)) {
//read 返回错误。 EWOULDBLOCK 是正常的错误,表明这个描述符还处在阻塞中,应该忽略该错误
if ( (n = read(sockfd, friptr, &fr[MAXLINE] - friptr)) < 0) {
if (errno != EWOULDBLOCK)
err_sys("read error on socket");
}
//read 返回 EOF,服务器返回数据结束
else if (n == 0) {
#ifdef VOL2
fprintf(stderr, "%s: EOF on socket\n", gf_time());
#endif
if (stdineof) //如果遇到来自服务器的 EOF ,并且已经在标准输入上遇到 EOF ,则正常结束;否则就是服务器过早终止了
return; /* normal termination */
else
err_quit("str_cli: server terminated prematurely");
}
//read 返回数据
else {
#ifdef VOL2
fprintf(stderr, "%s: read %d bytes from socket\n",
gf_time(), n);
#endif
friptr += n; //增加 friptr
FD_SET(STDOUT_FILENO, &wset); // 打开写描述符集中对应标准输出的位
}
}
/* end nonb2 */
/* include nonb3 */
//write 到标准输出
if (FD_ISSET(STDOUT_FILENO, &wset) && ( (n = friptr - froptr) > 0)) {
//write 返回错误。
if ( (nwritten = write(STDOUT_FILENO, froptr, n)) < 0) {
if (errno != EWOULDBLOCK)
err_sys("write error to stdout");
}
//write 成功
else {
#ifdef VOL2
fprintf(stderr, "%s: wrote %d bytes to stdout\n",
gf_time(), nwritten);
#endif
froptr += nwritten; //增加写出字节数
if (froptr == friptr)
froptr = friptr = fr; //如果输出指针追上输入指针, 这两个指针不同时恢复为指向缓冲区开始处
}
}
//write 到套接字
if (FD_ISSET(sockfd, &wset) && ( (n = toiptr - tooptr) > 0)) {
//write 失败
if ( (nwritten = write(sockfd, tooptr, n)) < 0) {
if (errno != EWOULDBLOCK)
err_sys("write error to socket");
}
//write 成功
else {
#ifdef VOL2
fprintf(stderr, "%s: wrote %d bytes to socket\n",
gf_time(), nwritten);
#endif
tooptr += nwritten;
if (tooptr == toiptr) { //
toiptr = tooptr = to;
if (stdineof) //如果标准输入已经读完,并且已经缓冲区的数据都已经发送给服务器,就关闭写套接字
Shutdown(sockfd, SHUT_WR); /* send FIN */
}
}
}
}
}
/* end nonb3 *//**
* TCP 使用两个进程 (fork)
**/
#include "unp.h"
void
str_cli(FILE *fp, int sockfd)
{
pid_t pid;
char sendline[MAXLINE], recvline[MAXLINE];
//子进程:从服务器读取数据并输出到 stdout
if ( (pid = Fork()) == 0) {
while (Readline(sockfd, recvline, MAXLINE) > 0)
Fputs(recvline, stdout);
kill(getppid(), SIGTERM); /* 子进程向父进程发送一个 SIGTERM 信号,终止父进程 */
exit(0);
}
//父进程:从 stdin 读取数据并发送到服务器
while (Fgets(sendline, MAXLINE, fp) != NULL)
Writen(sockfd, sendline, strlen(sendline));
//在 stdin 读取 EOF,输入结束,关闭写套接字
Shutdown(sockfd, SHUT_WR); /* EOF on stdin, send FIN */
//父进程完成数据凰调用 pause 让自己进入睡眠状态
pause();
return;
}
/**
* TCP 使用两个线程
**/
#include "unpthread.h"
void *copyto(void *);
static int sockfd; /* global for both threads to access */
static FILE *fp;
void
str_cli(FILE *fp_arg, int sockfd_arg)
{
char recvline[MAXLINE];
pthread_t tid;
//1.把参数保存在外部变量中
sockfd = sockfd_arg; /* copy arguments to externals */
fp = fp_arg;
//2.创建新线程
Pthread_create(&tid, NULL, copyto, NULL);
//3.主线程循环:从套接字到标准输出复制
while (Readline(sockfd, recvline, MAXLINE) > 0)
Fputs(recvline, stdout);
}
//4.copyto 线程:从标准输入到套接字复制
void *
copyto(void *arg)
{
char sendline[MAXLINE];
while (Fgets(sendline, MAXLINE, fp) != NULL)
Writen(sockfd, sendline, strlen(sendline));
Shutdown(sockfd, SHUT_WR); /* EOF on stdin, send FIN */
return(NULL);
/* 4return (i.e., thread terminates) when EOF on stdin */
}标签:style blog http color 使用 io 文件 for ar
原文地址:http://blog.csdn.net/zhengsenlie/article/details/38826819