笨办法学C 练习45:一个简单的TCP/IP客户端

练习45:一个简单的TCP/IP客户端

原文:Exercise 45: A Simple TCP/IP Client

译者:飞龙

我打算使用RingBuffer来创建一个非常简单的小型网络测试工具,叫做netclient。为此我需要向Makefile添加一些工具,来处理bin/目录下的小程序。

扩展Makefile

首先,为程序添加一些变量,就像单元测试的TESTSTEST_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.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_somewrite_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的工具可以用于建立这种服务器。另一种方法就是使用PythonRuby之类的语言创建一个简单的“垃圾服务器”,来产生垃圾数据,随机关闭连接,或者其它异常行为。

如果你找到了bug,在评论中报告它们,我会修复它。

附加题

  • 像我提到的那样,这里面有一些你不知道的函数,去查询他们。实际上,即使你知道它们也要查询。

  • valgrind下运行它来寻找错误。

  • 为函数添加各种防御性编程检查,来改进它们。

  • 使用getopt函数,运行用户提供选项来防止将\n转换为\r\n。这仅仅用于需要处理行尾的协议例如HTTP。有时你可能不想执行转换,所以要给用户一个选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398170.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javascript继承模式原理与示例深入剖析

原型链ECMAScript 中描述了原型链的概念&#xff0c;并将原型链作为实现继承的主要方法。 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。简单回顾一下构造函数、原型和实例的关系&#xff1a;每个构造函数都有一个原型对象&#xff0c;原型对象都包含一…

mysql innodb_sort_buffer_size_mysql优化---第7篇:参数 innodb_buffer_pool_instances设置

摘要&#xff1a;1 innodb_buffer_pool_instances可以开启多个内存缓冲池&#xff0c;把需要缓冲的数据hash到不同的缓冲池中&#xff0c;这样可以并行的内存读写。2 innodb_buffer_pool_instances参数显著的影响测试结果&#xff0c;特别是非常高的 I/O 负载时。3 实验环境下&…

open-falcon的插件机制

Plugin可以看做是对agent功能的扩充。对于业务系统的监控指标采集&#xff0c;最好不要做成plugin&#xff0c;而是把采集脚本放到业务程序发布包中&#xff0c;随着业务代码上线而上线&#xff0c;随着业务代码升级而升级&#xff0c;这样会比较容易管理。 1. 编写采集脚本 用…

从简单的信道预计说起

前面写了关于CP在OFDM中的应用&#xff0c;主要是记录一点零星的想法而已&#xff0c;今天突然想写点关于信道特性方面的东西。原因有下面几点&#xff1a; 1&#xff09;信道在仿真中的地位不容置疑&#xff0c;不同信道的条件下的仿真是很多课题的重点&#xff0c;自己差点儿…

python input函数赋值法_赋值法 - 静雅斋数学 - 博客园

前言赋值法是高中数学中比较常用的一种方法&#xff0c;使用“赋值法”的数学素材和知识点&#xff0c;散落在高中数学的几乎各个章节中&#xff0c;现对其进行整理&#xff0c;以便于学习。比如学习函数时可以赋值法给出单调性&#xff0c;奇偶性&#xff0c;周期性等&#xf…

Android surfaceview详解

周末看《精通Android游戏开发》(Pro Android Games)&#xff0c;里面讲到游戏的框架&#xff0c;其中一个重要的概念surfaceview,觉得不是很理解&#xff0c;于是花了一点时间研究了下&#xff0c;写下自己的心得。surface&#xff0c;这个单词的意思是浮在表面的&#xff0c;那…

ThinkPhp学习06

一、简单学习修改用户信息模块 1、编写UserAction.class.php 1 <?php2 3 class UserAction extends Action{4 public function index(){5 $mM(User);6 $arr$m->select();7 $this->assign(data,$arr);8 …

Spring MVC @SessionAttributes注解

SessionAttributes原理 默认情况下Spring MVC将模型中的数据存储到request域中。当一个请求结束后&#xff0c;数据就失效了。如果要跨页面使用。那么需要使用到session。而SessionAttributes注解就可以使得模型中的数据存储一份到session域中。 SessionAttributes参数 1、name…

C# 关于MVC框架的简单实例(计算器)

一、需求分析 实现效果 二、实现步骤 步骤一&#xff1a;新建项目--->Web---->空模板 步骤二&#xff1a;添加控制器 步骤三&#xff1a;根据控制器名称添加视图 步骤四&#xff1a;添加Models模型 编写具体的方法 using System;using System.Collections.Generic;using…

JS函数

函数&#xff1a; 函数是由事件驱动或者当它被调用时执行的可重复色代码块。 <head> <script> function hanshu() { alert("你好&#xff01;"); } </script> </head><body> <button οnclick"hanshu()">点击</but…

堆与二叉树(下)

接着上次的&#xff0c;这里主要介绍的是堆排序&#xff0c;二叉树的遍历&#xff0c;以及之前讲题时答应过的简单二叉树问题求解 堆排序 给一组数据&#xff0c;升序&#xff08;降序&#xff09;排列 思路 思考&#xff1a;如果排列升序&#xff0c;我们应该建什么堆&#x…

java new数组_Java如何使用new创建数组

java语言使用new操作符来创建数组&#xff0c;语法如下&#xff1a;arrayRefVar new dataType[arraySize];上面的语法语句做了两件事&#xff1a;一、使用dataType[arraySize]创建了一个数组。二、把新创建的数组的引用赋值给变量 arrayRefVar。数组变量的声明&#xff0c;和创…

hibernate select语句返回的类型

2019独角兽企业重金招聘Python工程师标准>>> Person类中包含有MyEvent这个类 public class Person{private Long id;private String name;private MyEvent myEvent; } 一、HQL from语句 1、结果类型&#xff1a;List<Person> from Person 或者 from Person…

RDLC系列之五 初试XAML

本章只讲解xaml部分&#xff0c;其余都和winform下一样 1.xaml代码 <Window x:Class"RDLC.WPF.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:r…

Android 数据库升级解决方案

转自&#xff1a;http://blog.csdn.net/leehong2005/article/details/9128501 请考虑如下情况&#xff1a; 在数据库升级时&#xff0c;不同版本的数据库&#xff0c;他们定义的表结构完全可能是不一样的&#xff0c;比如V1.0的表A有10个column&#xff0c;而在V1.1的表A有12个…

[php入门] 3、WAMP中的集成MySQL相关基础操作

前言&#xff1a;本文以小白视角了解WAMP集成开发环境中的MYSQL&#xff0c;涉及的面广而浅&#xff0c;算是导读性质。 1、启动运行熟悉WAMP中的MySQL 先有库、再有表、数据最终以记录的形式插入表中。其中对数据进行操作使用SQL语句&#xff0c;SQL是结构化的查询语言。 在wa…

apns java 证书_APNS推送服务证书制作 图文详解教程(新)

iOS消息推送的工作机制可以简单的用下图来概括&#xff1a;Provider是指某个iPhone软件的Push服务器&#xff0c;APNS是Apple Push Notification Service的缩写&#xff0c;是苹果的服务器。上图可以分为三个阶段&#xff1a;第一阶段&#xff1a;应用程序把要发送的消息、目的…

Android近场通信---NFC基础(二)(转)

转自 http://blog.csdn.net/think_soft/article/details/8171256 应用程序如何调度NFC标签 当标签调度系统完成对NFC标签和它的标识信息封装的Intent对象的创建时&#xff0c;它会把该Intent对象发送给感兴趣的应用程序。如果有多个应用程序能够处理该Intent对象&#xff0c;就…

:base(参数)

:base(必须有值)&#xff1a;作用是将父类的值继承过来&#xff0c;如果不在构造函数中加入&#xff1a;base(变量) 的话&#xff0c;原父类中的 Model则无法继承过来。 例如&#xff1a;在父类MSG_Model,有连个属性&#xff0c;如图 1.子类构造函数不写:base(参数) 2.1.子类构…

MVC5 + EF6 完整入门教程三

MVC5 EF6 完整入门教程三 原文:MVC5 EF6 完整入门教程三期待已久的EF终于来了。 学完本篇文章&#xff0c;你将会掌握基于EF数据模型的完整开发流程。 本次将会完成EF数据模型的搭建和使用。 基于这个模型&#xff0c;将之前的示例添加数据库查询验证功能。 文章提纲 概…