<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 練習45:一個簡單的TCP/IP客戶端 > 原文:[Exercise 45: A Simple TCP/IP Client](http://c.learncodethehardway.org/book/ex45.html) > 譯者:[飛龍](https://github.com/wizardforcel) 我打算使用`RingBuffer`來創建一個非常簡單的小型網絡測試工具,叫做`netclient`。為此我需要向`Makefile`添加一些工具,來處理`bin/`目錄下的小程序。 ## 擴展Makefile 首先,為程序添加一些變量,就像單元測試的`TESTS`和`TEST_SRC`變量: ```Makefile PROGRAMS_SRC=$(wildcard bin/*.c) PROGRAMS=$(patsubst %.c,%,$(PROGRAMS_SRC)) ``` 之后你可能想要添加`PROGRAMS`到所有目標中: ```makefile all: $(TARGET) $(SO_TARGET) tests $(PROGRAMS) ``` 之后在`clean`目標中向`rm`那一行添加`PROGRAMS`: ```makefile rm -rf build $(OBJECTS) $(TESTS) $(PROGRAMS) ``` 最后你還需要在最后添加一個目標來構建它們: ```makefile $(PROGRAMS): CFLAGS += $(TARGET) ``` 做了這些修改你就能夠將`.c`文件扔到`bin`中,并且編譯它們以及為其鏈接庫文件,就像測試那樣。 ## netclient 代碼 netclient的代碼是這樣的: ```c #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.h> struct 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, "Can't 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, "Can't 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上得到一個特殊的文件: ```sh $ $ ./bin/netclient learncodethehardway.org 80 GET /ex45.txt HTTP/1.1 Host: learncodethehardway.org HTTP/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.5 Learn 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。有時你可能不想執行轉換,所以要給用戶一個選擇。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看