我们知道许多应用程序,例如E-mail、Web和即时通信都依靠网络才能实现。这些应用程序中的每一个都依赖一种特定的网络协议,但每个协议都使用相同的常规网络传输方法。许多人都没有意识到网络协议本身存在漏洞。本文将会学习如何使用套接字使应用程序访问网络以及如何处理常见的网络漏洞。
650) this.width=650;" alt="" src="http://img1.51cto.com/attachment/201106/124510184.jpg" border="0" />
图1  OSI模型
1.套接字
套接字是通过操作系统(OS)完成网络通信的一种标准方法。可以将套接字看作是与连接相连的一个终端,就像是操作员配电盘上的一个插座一样。但是这些套接字只是程序员的抽象称呼,它们负责有文描述的OSI模型的所有基本细节。对程序员来说,可以使用一个套接字通过网络发送或接收数据。这些数据在较低的层(由操作系统处理)之上的会话层(5)传输,该层负责路由。有几种不同的套接字,它们决定了传输层的结构。最常见的类型是流套接字和数据报套接字。
流套接字提供了可靠的双向通信,这类似于您和他人打电话。一方向另一方发起连接,建立连接之后,任何一方都可以和另一方通信。此外,您所说的话实际上是否到达目的地能够得到快速证实。流套接字使用一种称为传输控制协议(Transmission Control Protocol,TCP)的标准通信协议,这个协议存在于OSI模型的传输层(4)。在计算机网络上,数据通常以我们称之为包的大数据块的形式传输。TCP被设计为数据包按顺序到达目的地并且无差错,就像在电话中讲话时,单词以它们被说出的顺序到达另一端一样。Web服务器、邮件服务器以及它们各自的客户应用程序都使用TCP和流套接字进行通信。
另一种常见的套接字类型是数据报套接字。使用数据报套接字通信更像是邮寄一封信而不是打电话。连接是单向的并且不可靠。如果您寄了几封信,您将不能确定它们是否按照和邮寄时相同的顺序到达目的地,甚至连能否被送达目的地也不能保证。邮政服务相当可靠,但Intemet并不可靠。数据报套接字在传输层(4)上使用另一种称为UDP的标准协议来代替TCP。UDP代表用户数据报协议(User Datagram Protocol),意味着可以用它来创建自定义协议。这个协议非常基本并且是轻量级的,它只内置了很少的保护措施。它并不是一种真正的连接,只是一种从一端向另一端发送数据的基本方法。使用数据报套接字时,协议中的系统开销非常少,但协议完成的功能也不多。如果程序需要证实另一方接收到了数据包,必须编程使另一方回送一个确认包。有些情况下,可以接受数据包的丢失。数据报套接字和UDP普遍用于网络游戏和流媒体,因为开发人员可以根据需要精确地修整他们的通信,而不会存在像TCP那样的固有系统开销。
2.套接字函数
在C语言中,套接字的行为类似于文件,因为它们使用文件描述符来标识它们自己。套接字的行为与文件非常相似,实际上利用套接字文件描述符,可以使用read()和write()函数接收和发送数据。但是,有几个函数是专门设计用来处理套接字的。在/usr/include/sys/socket.h文件中有这些函数原型的定义。
extern int socket (int __domain, int __type, int __protocol) __THROW;
用于创建一个新套接字,返回一个表牙示套接字后的文件描述符,错误时遣返回-1。extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);
将一个套接字(由文件描述符fd指定)连接到远程主机。成功返回0,错误返回-1。
Listen(int fd,int backlog_queue_size)
侦听传入的连接并将连接请求排队,:直到数量达到backlog_queue_size。成功返回0,错误返回-1。
extern int accept (int __fd, __SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len);
一个绑定的端口上接受一个传入连接。远程主机的地址信息写入remote_host结构中,地址结构的实际大小写入到addr_len中。这个函数返回一个新套接字文件描述符来标识已经连接的套接字,错误返回-1。
extern ssize_t send (int __fd, __const void *__buf, size_t __n, int __flags);
从*__buf向套接字fd发送n个字节,返回值为发送的字节数,错误返回-1。
extern ssize_t recv (int __fd, void *__buf, size_t __n, int __flags);
从套接字fd接收n个字节到*__buf中,返回值为收到的字节数,错误返回-1。
使用socket()函数创建套接字时,必须指定套接字的域(domain)、类型(type)和切协议( protocol)。域指的是套接字的协议族。套接字可以使用各种协议进行通信,从浏览Web时使用的标准Internet协议到诸如AX.25,这样的业余无线电协议(如果您是一个无线电发烧友)。这些协议族在bits/socket.h中定义,它自动包含在sys/socket.h中。
/usr/include/bits/socket.h 片段1
/* Protocol families.  */
#define PF_UNSPEC 0 /* Unspecified.  */
#define PF_LOCAL 1 /* Local to host (pipes and file-domain).  */
#define PF_UNIX PF_LOCAL /* Old BSD name for PF_LOCAL.  */
#define PF_FILE PF_LOCAL /* Another non-standard name for PF_LOCAL.  */
#define PF_INET 2 /* IP protocol family.  */
#define PF_AX25 3 /* Amateur Radio AX.25.  */
#define PF_IPX 4 /* Novell Internet Protocol.  */
#define PF_APPLETALK 5 /* Appletalk DDP.  */
#define PF_NETROM 6 /* Amateur radio NetROM.  */
#define PF_BRIDGE 7 /* Multiprotocol bridge.  */
#define PF_ATMPVC 8 /* ATM PVCs.  */
#define PF_X25 9 /* Reserved for X.25 project.  */
#define PF_INET6 10 /* IP version 6.  */
如前所述,虽然流套接霉字和数据报套接字最常使用,但还有其他几种类型的套接字。套接字的类型也定义在bits/socket.h中(上面代码中的/*comment*/是另一种形式的注释,它将所有处于星号之间的内容作为注释)。/usr/include/bits/socket.h 片段2
/* Types of sockets.  */
enum __socket_type
{
  SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
   byte streams.  */
#define SOCK_STREAM SOCK_STREAM
  SOCK_DGRAM = 2, /* Connectionless, unreliable datagrams
   of fixed maximum length.  */
#define SOCK_DGRAM SOCK_DGRAM
  SOCK_RAW = 3, /* Raw protocol interface.  */
#define SOCK_RAW SOCK_RAW
  SOCK_RDM = 4, /* Reliably-delivered messages.  */
#define SOCK_RDM SOCK_RDM
  SOCK_SEQPACKET = 5, /* Sequenced, reliable, connection-based,
   datagrams of fixed maximum length.  */
#define SOCK_SEQPACKET SOCK_SEQPACKET
  SOCK_PACKET = 10 /* Linux specific way of getting packets
   at the dev level.  For writing rarp and
   other similar things on the user level. */
#define SOCK_PACKET SOCK_PACKET
};
Socket()函数的最后一个参数是协议,该参数通常为0。该函数的详细说明书允许使用一个协议族中的多个协议,因此这个参数用来从协议族中选择一个协议。但是,实际上大多数协议族中仅有一个协议,这意味着这个参数通常被设置为0,即选用协议族列表中第一个也是唯一的一个协议。本文使用套接字所做的事情也是这种情况,所以在我们的例子中这个参数总是0。
3.套接字地址
许多套接字函数引用一个sockad出结构来传递定义了一个主机的地址信息。这个结构也定义在bits/socket.h中,如后面所示。
/usr/include/bits/socket.h 片段3
/* Get the definition of the macro to define the common sockaddr members.  */
#include <bits/sockaddr.h>
/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_); /* Common data: address family and length.  */
    char sa_data[14]; /* Address data.  */
  };
宏SOCKADDR_COMMON在文件bits/sockaddr.h中定义,其本质是将参数转换为一个无符号短整型数。这个值定义了地址的地址族,结构的其余部分用于保存地址数据。因为套接字可以使用各种协议族进行通信,根据地址族的不同,每个协议族都有自己的定义终端地址的方法,所以必须将地址定义为变量。可用的地址族也在bits/socket.h中定义,它们通常直接转换成相应的协议族。
/usr/include/bits/socket.h  片段4
/* Address families.  */
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
#define AF_ROSE PF_ROSE
#define AF_DECnet PF_DECnet
#define AF_NETBEUI PF_NETBEUI
#define AF_SECURITY PF_SECURITY
#define AF_KEY PF_KEY
#define AF_NETLINK PF_NETLINK
#define AF_ROUTE PF_ROUTE
#define AF_PACKET PF_PACKET
#define AF_ASH PF_ASH
#define AF_ECONET PF_ECONET
#define AF_ATMSVC PF_ATMSVC
#define AF_SNA PF_SNA
#define AF_IRDA PF_IRDA
#define AF_PPPOX PF_PPPOX
#define AF_WANPIPE PF_WANPIPE
#define AF_BLUETOOTH PF_BLUETOOTH
#define AF_MAX PF_MAX
因为地址可以包含不同类型的信息(这取决于地址族),所以有其他几种地址结构,它们在地址数据部分包含了来自于sockaddr结构的公共元素以及地址族的特殊信息。这些结构大小相同,因此可以将它们从一种类型强制转换到另一种类型。这意味着socket()函数会简单地接受一个指向sockaddr结构的指针,而这个指针事实上可以指向一个IPv4、IPv6或X.25地址。这样就允许套接字函数操作各种协议了。

本文出自 “李晨光原创技术博客” 博客,谢绝转载!

如何使用Linux套接字?的更多相关文章

  1. linux 套接字编程入门--Hello World

    下述代码是linux套接字编程的入门代码.分为服务端和客户端源码. 服务端代码的主要流程是绑定ip地址和端口号建立套接字,等待客户端发起访问.接受客户端请求之后,向客户端发送字符串"hell ...

  2. Linux 套接字编程中的 5 个隐患(转)

    本文转自IBM博文Linux 套接字编程中的 5 个隐患. “在异构环境中开发可靠的网络应用程序”. Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是开发新 ...

  3. 【 Linux 】Linux套接字简要说明

    Linux套接字    源IP地址和目的IP地址以及源端口和目标端口号的组合称为套接字.其作用于标识客户端请求的服务器和服务. 套接字,支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间 ...

  4. socket - Linux 套接字

    总览 #include <sys/socket.h> mysocket = socket(int socket_family, int socket_type, int protocol) ...

  5. (转载)Linux 套接字编程中的 5 个隐患

    在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系统的标准特性.事实上,很难找到一种不支持 Sockets API 的现代语言.该 API 相当简单,但新的开 ...

  6. Linux套接字编程

    网络中的进程是如何通信的? 在网络中进程之间进行通信的时候,那么每个通信的进程必须知道它要和哪个计算机上的哪个进程通信.否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行 ...

  7. Linux 套接字编程中的 5 个隐患

    http://www.ibm.com/developerworks/cn/linux/l-sockpit/ 在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系 ...

  8. Linux 套接字编程中要注意的细节

    隐患 1.忽略返回状态 第一个隐患很明显,但它是开发新手最容易犯的一个错误.如果您忽略函数的返回状态,当它们失败或部分成功的时候,您也许会迷失.反过来,这可能传播错误,使定位问题的源头变得困难. 捕获 ...

  9. windows和linux套接字中的select机制浅析

    先来谈谈为什么会出现select函数,也就是select是解决什么问题的? 平常使用的recv函数时阻塞的,也就是如果没有数据可读,recv就会一直阻塞在那里,这是如果有另外一个连接过来,就得一直等待 ...

  10. Linux 套接字通信笔记(一)

    协议 TCP(传输控制协议),UDP(用户数据包协议)为传输层重要的两大协议,向上为HTTP提供底层协议,向下为数据链路层封装底层接口,乃是通信重中之重.TCP是面向流传输的协议,在编程中形象化为St ...

随机推荐

  1. Java 8 时间日期库的20个使用演示样例

    除了lambda表达式,stream以及几个小的改进之外,Java 8还引入了一套全新的时间日期API,在本篇教程中我们将通过几个简单的任务演示样例来学习怎样使用Java 8的这套API.Java对日 ...

  2. BZOJ 1305 二分+网络流

    思路: 建图我根本没有想到啊--. (我是不会告诉你我借鉴了一下题解的思路) 把每个人拆成喜欢的和不喜欢的点 男 喜欢 向 男 不喜欢 连 边权为k的边 如果男喜欢女 那么 男喜欢向 女喜欢 连 1 ...

  3. 洛谷P2633 Count on a tree

    题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个 ...

  4. logAnalyzer日志管理系统配置实例

    LogAnalyzer日志管理系统配置实例 上个月我写过一篇<利用EventlogAnalyzer分析Linux日志>一文深受大家喜欢,今天我再次为大家讲解Linux系统下的一款开源的日志 ...

  5. Android控件-ViewPager(仿微信引导界面)

    什么是ViewPager? ViewPager是安卓3.0之后提供的新特性,继承自ViewGroup,专门用以实现左右滑动切换View的效果. 如果想向下兼容就必须要android-support-v ...

  6. 分享一个正则 选择html中所有的单标签

    var str = /\B<.+?>/g;

  7. codeforces 140E.New Year Garland

    传送门: 解题思路: 要求相邻两行小球颜色集合不同,并且限制行内小球相邻不同. 由此可得:每行小球排列都是独立与外界的, 所以答案应该是对于所有行的颜色集合分类,在将行内的答案乘到上面. 先考虑如何分 ...

  8. 紫书 习题 10-25 UVa 1575 (有重复元素的全排列+暴搜)

    我一开始以为有什么很牛逼的方法来做,然后一直没有思路 后来看了https://blog.csdn.net/zju2016/article/details/78562932的博客 竟然是暴搜?????? ...

  9. Swift学习笔记(7)--控制流

    1.For循环 //1.条件递增 for var index = 0; index < 3; ++index { println("index is \(index)") } ...

  10. qt qlineedit只输入数字

    lineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9]+$")));