如何使用Linux套接字?

简介:




    我们知道许多应用程序,例如E-mailWeb和即时通信都依靠网络才能实现。这些应用程序中的每一个都依赖一种特定的网络协议,但每个协议都使用相同的常规网络传输方法。许多人都没有意识到网络协议本身存在漏洞。本文将会学习如何使用套接字使应用程序访问网络以及如何处理常见的网络漏洞。

图1  OSI模型
1.套接字
套接字是通过操作系统(OS)完成网络通信的一种标准方法。可以将套接字看作是与连接相连的一个终端,就像是操作员配电盘上的一个插座一样。但是这些套接字只是程序员的抽象称呼,它们负责有文描述的OSI模型的所有基本细节。对程序员来说,可以使用一个套接字通过网络发送或接收数据。这些数据在较低的层(由操作系统处理)之上的会话层(5)传输,该层负责路由。有几种不同的套接字,它们决定了传输层的结构。最常见的类型是流套接字和数据报套接字。
流套接字提供了可靠的双向通信,这类似于您和他人打电话。一方向另一方发起连接,建立连接之后,任何一方都可以和另一方通信。此外,您所说的话实际上是否到达目的地能够得到快速证实。流套接字使用一种称为传输控制协议(Transmission Control ProtocolTCP)的标准通信协议,这个协议存在于OSI模型的传输层(4)。在计算机网络上,数据通常以我们称之为包的大数据块的形式传输。TCP被设计为数据包按顺序到达目的地并且无差错,就像在电话中讲话时,单词以它们被说出的顺序到达另一端一样。Web服务器、邮件服务器以及它们各自的客户应用程序都使用TCP和流套接字进行通信。
另一种常见的套接字类型是数据报套接字。使用数据报套接字通信更像是邮寄一封信而不是打电话。连接是单向的并且不可靠。如果您寄了几封信,您将不能确定它们是否按照和邮寄时相同的顺序到达目的地,甚至连能否被送达目的地也不能保证。邮政服务相当可靠,但Intemet并不可靠。数据报套接字在传输层(4)上使用另一种称为UDP的标准协议来代替TCPUDP代表用户数据报协议(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
L isten(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
};
S ocket()函数的最后一个参数是协议,该参数通常为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地址。这样就允许套接字函数操作各种协议了。



 本文转自 李晨光 51CTO博客,原文链接: http://blog.51cto.com/chenguang/581159 ,如需转载请自行联系原作者

相关文章
|
7月前
|
网络协议 Linux C++
【Linux】网络编程套接字
【Linux】网络编程套接字
|
6天前
|
存储 网络协议 Unix
【Linux 网络】网络编程套接字 -- 详解
【Linux 网络】网络编程套接字 -- 详解
|
14天前
|
网络协议 Java Linux
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
16 0
|
14天前
|
存储 网络协议 算法
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
17 0
|
14天前
|
网络协议 算法 Linux
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
20 0
|
14天前
|
存储 算法 网络协议
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
19 0
|
14天前
|
存储 网络协议 Unix
【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)
【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)
9 0
|
6月前
|
网络协议 Unix Linux
从零开始学习 Linux 内核套接字:掌握网络编程的必备技能
从零开始学习 Linux 内核套接字:掌握网络编程的必备技能
|
11月前
|
Unix Linux
Linux本地套接字(Unix域套接字)----SOCK_DGRAM方式
这里介绍一下Linux进程间通信的socket方式---Local socket。这篇主要是介绍下SOCK_DGRAM方式的通信,即数据包的方式(与UDP类似),面向无连接。
237 0
|
网络协议 Unix Linux
Android C++ 系列:Linux Socket 编程(二)网络套接字函数
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描 述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调 用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表 示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。
160 0