琪觅公司网站开发,网站建设电话营销话术,网站建设与管理vs2010,做网站niche练习45#xff1a;一个简单的TCP/IP客户端 原文#xff1a;Exercise 45: A Simple TCP/IP Client 译者#xff1a;飞龙 我打算使用RingBuffer来创建一个非常简单的小型网络测试工具#xff0c;叫做netclient。为此我需要向Makefile添加一些工具#xff0c;来处理bin/目录下… 练习45一个简单的TCP/IP客户端 原文Exercise 45: A Simple TCP/IP Client 译者飞龙 我打算使用RingBuffer来创建一个非常简单的小型网络测试工具叫做netclient。为此我需要向Makefile添加一些工具来处理bin/目录下的小程序。 扩展Makefile 首先为程序添加一些变量就像单元测试的TESTS和TEST_SRC变量 PROGRAMS_SRC$(wildcard bin/*.c)
PROGRAMS$(patsubst %.c,%,$(PROGRAMS_SRC)) 之后你可能想要添加PROGRAMS到所有目标中 all: $(TARGET) $(SO_TARGET) tests $(PROGRAMS) 之后在clean目标中向rm那一行添加PROGRAMS rm -rf build $(OBJECTS) $(TESTS) $(PROGRAMS) 最后你还需要在最后添加一个目标来构建它们 $(PROGRAMS): CFLAGS $(TARGET) 做了这些修改你就能够将.c文件扔到bin中并且编译它们以及为其链接库文件就像测试那样。 netclient 代码 netclient的代码是这样的 #undef NDEBUG
#include stdlib.h
#include sys/select.h
#include stdio.h
#include lcthw/ringbuffer.h
#include lcthw/dbg.h
#include sys/socket.h
#include sys/types.h
#include sys/uio.h
#include arpa/inet.h
#include netdb.h
#include unistd.h
#include fcntl.hstruct tagbstring NL bsStatic(\n);
struct tagbstring CRLF bsStatic(\r\n);int nonblock(int fd) {int flags fcntl(fd, F_GETFL, 0);check(flags 0, Invalid flags on nonblock.);int rc fcntl(fd, F_SETFL, flags | O_NONBLOCK);check(rc 0, Cant set nonblocking.);return 0;
error:return -1;
}int client_connect(char *host, char *port)
{int rc 0;struct addrinfo *addr NULL;rc getaddrinfo(host, port, NULL, addr);check(rc 0, Failed to lookup %s:%s, host, port);int sock socket(AF_INET, SOCK_STREAM, 0);check(sock 0, Cannot create a socket.);rc connect(sock, addr-ai_addr, addr-ai_addrlen);check(rc 0, Connect failed.);rc nonblock(sock);check(rc 0, Cant set nonblocking.);freeaddrinfo(addr);return sock;error:freeaddrinfo(addr);return -1;
}int read_some(RingBuffer *buffer, int fd, int is_socket)
{int rc 0;if(RingBuffer_available_data(buffer) 0) {buffer-start buffer-end 0;}if(is_socket) {rc recv(fd, RingBuffer_starts_at(buffer), RingBuffer_available_space(buffer), 0);} else {rc read(fd, RingBuffer_starts_at(buffer), RingBuffer_available_space(buffer));}check(rc 0, Failed to read from fd: %d, fd);RingBuffer_commit_write(buffer, rc);return rc;error:return -1;
}int write_some(RingBuffer *buffer, int fd, int is_socket)
{int rc 0;bstring data RingBuffer_get_all(buffer);check(data ! NULL, Failed to get from the buffer.);check(bfindreplace(data, NL, CRLF, 0) BSTR_OK, Failed to replace NL.);if(is_socket) {rc send(fd, bdata(data), blength(data), 0);} else {rc write(fd, bdata(data), blength(data));}check(rc blength(data), Failed to write everything to fd: %d., fd);bdestroy(data);return rc;error:return -1;
}int main(int argc, char *argv[])
{fd_set allreads;fd_set readmask;int socket 0;int rc 0;RingBuffer *in_rb RingBuffer_create(1024 * 10);RingBuffer *sock_rb RingBuffer_create(1024 * 10);check(argc 3, USAGE: netclient host port);socket client_connect(argv[1], argv[2]);check(socket 0, connect to %s:%s failed., argv[1], argv[2]);FD_ZERO(allreads);FD_SET(socket, allreads);FD_SET(0, allreads);while(1) {readmask allreads;rc select(socket 1, readmask, NULL, NULL, NULL);check(rc 0, select failed.);if(FD_ISSET(0, readmask)) {rc read_some(in_rb, 0, 0);check_debug(rc ! -1, Failed to read from stdin.);}if(FD_ISSET(socket, readmask)) {rc read_some(sock_rb, socket, 0);check_debug(rc ! -1, Failed to read from socket.);}while(!RingBuffer_empty(sock_rb)) {rc write_some(sock_rb, 1, 0);check_debug(rc ! -1, Failed to write to stdout.);}while(!RingBuffer_empty(in_rb)) {rc write_some(in_rb, socket, 1);check_debug(rc ! -1, Failed to write to socket.);}}return 0;error:return -1;
} 代码中使用了select来处理stdin文件描述符0和用于和服务器交互的socket中的事件。它使用了RingBuffer来储存和复制数据并且你可以认为read_some和write_some函数都是RingBuffer中相似函数的原型。 在这一小段代码中可能有一些你并不知道的网络函数。当你碰到不知道的函数时在手册页上查询它来确保你理解了它。这一小段代码可能需要让你研究用于小型服务器编程的所有C语言API。 你会看到什么 如果你完成了所有构建测试的最快方式就是看看你能否从learncodethehardway.org上得到一个特殊的文件 $
$ ./bin/netclient learncodethehardway.org 80
GET /ex45.txt HTTP/1.1
Host: learncodethehardway.orgHTTP/1.1 200 OK
Date: Fri, 27 Apr 2012 00:41:25 GMT
Content-Type: text/plain
Content-Length: 41
Last-Modified: Fri, 27 Apr 2012 00:42:11 GMT
ETag: 4f99eb63-29
Server: Mongrel2/1.7.5Learn C The Hard Way, Exercise 45 works.
^C
$ 这里我所做的事情是键入创建/ex45.txt的HTTP请求所需的语法在Host:请求航之后按下ENTER键来输入空行。接着我获取相应包括响应头和内容。最后我按下CTRL-C来退出。 如何使它崩溃 这段代码肯定含有bug但是当前在本书的草稿中我会继续完成它。与此同时尝试分析代码并且用其它服务器来击溃它。一种叫做netcat的工具可以用于建立这种服务器。另一种方法就是使用Python或Ruby之类的语言创建一个简单的“垃圾服务器”来产生垃圾数据随机关闭连接或者其它异常行为。 如果你找到了bug在评论中报告它们我会修复它。 附加题 像我提到的那样这里面有一些你不知道的函数去查询他们。实际上即使你知道它们也要查询。在valgrind下运行它来寻找错误。为函数添加各种防御性编程检查来改进它们。使用getopt函数运行用户提供选项来防止将\n转换为\r\n。这仅仅用于需要处理行尾的协议例如HTTP。有时你可能不想执行转换所以要给用户一个选择。