套接字(socket)是一种通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行。Linux所提供的功能(如打印服务、连接数据库和提供Web页面)和网络工具(如用于远程登录的rlogin和用于文件传输的ftp)通常都是通过套接字来进行通信的。
套接字的创建和使用与管道是有区别的,因为套接字明确地将客户和服务器区分开来。套接字机制可以实现将多个客户连接到一个服务器。
域指定套接字通信中使用的网络介质。最常见的套接字域是AF_INET,它指的是Internet网络,许多Linux局域网使用的都是该网络,当然,因特网自身用的也是它。其底层的协议——网际协议(IP)只有一个地址族,它使用一种特定的方式来指定网络中的计算机,即人们常说的IP地址。
一个套接字域可能有多种不同的通信方式,而每种通信方式又有其不同的特性。但AF_UNIX域的套接字没有这样的问题,它们提供了一个可靠的双向通信路径。在网络域中,我们就需要注意底层网络的特性,以及不同的两种通信机制:流(stream)和数据报(datagram)。它们有着截然不同的服务层次。
流套接字(在某些方面类似于标准的输入/输出流)提供的是一个有序、可靠、双向字节流的连接。因此,发送的数据可以确保不会丢失、复制或乱序到达,并且在这一过程中发生的错误也不会显示出来。大的消息将被分片、传输、再重组。这很像一个文件流,它接收大量的数据,因为它们在编写网络程序时是最常用的。
TCP/IP代表的是传输控制协议/网际协议。IP协议是针对数据包的底层协议,它提供从一台计算机通过网络到达另一台计算机的路由。TCP协议提供排序,流控和重传,以确保大数据的传输可以完整地到达目的地或报告一个适当的错误条件。
与流套接字相反,由类型SOCK_DGRAM指定的数据报套接字不建立和维持一个连接。它对可以发送的数据报的长度有限制。数据报作为一个单独的网络消息被传输,它可能会丢失、复制或乱序到达。
数据报套接字是在AF_INET域中通过UDP/IP连接实现的,它提供的是一种无序的不可靠服务(UDP代表的是用户数据报协议)。但从资源的角度来看,相对来说它们开销比较小,因为不需要维持网络连接。而且因为无需花费时间来建立连接,所以它们的速度也很快。
数据报适用于信息服务中“单次”(singl-shot)查询,它主要用来提供日常状态信息或执行低优先级的日志记录。它的优点是服务器的崩溃不会给客户造成不便,也不会要求客户重启,因为基于数据报的服务器通常不保留连接信息,所以它们可以在不打扰其客户的前提下停止并重启。
只要底层的传输机制允许不止一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。那么这里提供了UNIX网络套接字和文件系统套接字,它们不需要你选择一个特定的协议,只需要使用其默认值即可。(这里将不详细讨论这两种)
socket系统调用创建一个套接字并返回一个描述符,该描述符可以用来访问该套接字。
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int protocol);
创建的套接字是一条通信线路的一个端点,domain参数指定协议族,type参数指定这个套接字的通信类型,protocol参数指定使用的协议。
domain参数可以指定的协议族如下图:

最常见的套接字域是AF_UNIX和AF_INET,前者用于通过UNIX和Linux文件系统实现的本地套接字,后者用于UNIX网络套接字。AF_INET套接字可以用于通过包括因特网在内的TCP/IP网络进行通信的程序。微软Windowns系统的Windowns接口也提供了对这个套接字域的访问功能。
socket函数的参数type指定用于新套接字的通信特性。它的取值包括SOCK_STREAM和SOCK_DGRAM。
SOCK_STREAM是一个有序、可靠、面向连接 的双向字节流,对AF_INET域套接字来说,它默认是通过一个TCP连接来提供这一特性的,TCP连接在两个流套接字端点之间建立。数据可以通过套接字连接进行双向传递。TCP协议所提供的机制可以用于分片和重组长消息,并且可以重传可能在网络中丢失的数据。
socket系统调用返回一个描述符,它在许多方面都类似于底层的文件描述符。当这个套接字连接到另一端的套接字后,我们就可以用read和write系统调用,通过这个描述符来在套接字上发送和接收数据了。close系统调用用于结束套接字连接。
每个套接字域都有其自己的地址格式。对于AF_UNIX域套接字来说,它的地址由结构sockaddr_un来描述,该结构定义在头文件sys/un.h中。
struct sockaddr_un{
sa_family_t sun_family; /* AF_UNIX */
char sun_path[]; /* pathname */
};
因此,对套接字进行处理的系统调用可能需要接受不同类型的地址,每种地址格式都使用一种类似的结构来描述,它们都以一个指定地址类型(套接字域)的成员(在本例中是sun_family)开始。在AF_UNIX域中,套接字地址由结构中的sun_path成员中的文件名所指定。
在当前的Linux系统中,由X/Open规范定义的类型sa_family_t在头文件sys/un.h中声明,它是短整型。此外,sun_path指定的路径名长度也是有限制的(Linux规定的是108个字符,其他系统可能使用的是更清楚的常量,如UNIX_MAX_PATH)。因为地址结构的长度不一致,所以许多套接字调用需要用到一个用来复制特定地址结构的长度变量或将它作为一个输出。
在AF_INET域中,套接字地址由结构sockaddr_in来指定,该结构定义在头文件netinet/in.h中,它至少包含以下几个成员:
struct sockaddr_in{
short int sin_family; /* AF_INEX */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
};
IP地址结构in_addr被定义为:
struct in_addr{
unsigned long int s_addr;
};
IP地址中的4个字节组成一个32位的值。一个AF_INET套接字由它的域、IP地址和端口号来完全确定。从应用程序的角度来看,所有套接字的行为就像文件描述符一样,并且通过一个唯一的整数值来区分。
要想让通过sockket调用创建的套接字可以被其他进程使用,服务器程序就必须给该套接字命名。这样,AF_UNIX套接字就会关联到一个文件系统的命名。
#include <sys/socket.h>
int bind(int socket, const struct sockaddr *address, size_t address_len);
bind系统调用把参数address中的地址分配给文件描述符socket关联的未命名套接字。地址结构的长度由参数address_len传递。
地址的长度和格式取决于地址族。bind调用需要将一个特定的地址结构指针转换为指向通用地址类型(struct sockaddr *)。
bind调用在成功时返回0,失败时返回-1并设置errno为下表中的一个值。

AF_UNIX域套接字还有其他一些错误代码,如下表。

为了能够在套接字上接受进入的连接,服务器程序必须创建一个队列来保存未处理的请求。它用listen系统调用来完成这一工作。
#include <sys/socket.h>
int listen(int socket, int backlog);
Linux系统可能会对队列中可以容纳的未处理连接的最大数目做出限制。为了遵守这个最大值限制,listen函数将队列长度设置为backlog参数的值。在套接字队列中,等待处理的进入连接的个数最多不能超过这个数字。再往后的连接将被拒绝,导致客户的连接请求失败。listen函数提供的这种机制允许当服务器程序正忙处理前一个客户请求的时候,将后续的客户连接放入队列等待处理。backlog参数常用的值为5.
listen函数在成功时返回0,失败时返回-1.错误代码包括EBADF、EINVAL和ENOTSOCK,其含义与上面bind系统调用中说明的一样。
一旦服务器程序创建并命名了套接字之后,它就可以通过accept系统调用来等待客户建立对该套接字的连接。
#include <sys/socket.h>
int accept(int socket, struct sockaddr *address, size_t *address_len);
accept系统调用只有当有客户程序试图连接到由socket参数指定的套接字上时才返回。这里的客户是值在套接字队列中排第一个的未处理连接。accept函数将创建一个新套接字来与该客户进行通信,并且返回新套接字的描述符。新套接字的类型和服务器监听套接字类型一样的。
套接字必须事先由bind调用命名,并且由listen调用给它分配一个连接队列。连接客户的地址将被放入address参数指向的sockaddr结构中。如果我们不关心客户的地址,也可以将address参数指定为空指针。
参数addr_len指定客户结构的长度。如果客户地址的长度超过这个值,它将被截断。所以在调用accept之前,address_len必须被设置为预期的地址长度。当这个调用返回时,address_len将被设置为连接客户地址结构的实际长度。
如果套接字队列中没有未处理的连接,accept将阻塞(程序将1暂停)直到有客户建立连接为止。我们可以通过对套接字文件描述符设置O_NONBLOCK标志来改变这一行为,使用的函数是fcnt1,如下:
int flags = fcnt1(socket, F_GETFL, 0);
fcnt1(socket, F_SETFL, O_NONBLOCK|flags);
当有未处理的客户连接时,accept函数将返回一个新的套接字文件描述符。发生错误时,accept函数将返回-1,可能的错误情况大部分与bind、listen调用类似,其他的错误有EWOULDBLOCK和EINTR。前者是当指定了O_NONBLOCK标志,但队列中没有未处理连接时产生的错误。后者是当进程阻塞在accept调用时,执行被中断而产生的错误。
客户程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器。它们通过connect调用来完成这一工作。
#include <sys/socket.h>
int connect(int socket, const struct sockaddr *address, size_t address_len);
参数socket指定的套接字将连接到参数address指定的服务器套接字,address指向的结构的长度由参数address_len指定。参数socket指定的套接字必须是通过socket调用获得的一个有效的文件描述符。
成功时,connect调用返回0,失败时返回-1.可能的错误代码见下表。

如果连接不能立刻建立,connect调用将阻塞一段不确定的超时时间。一旦这个超时时间到达,连接将被放弃,connect调用失败。但如果connect调用被一个信号中断,而该信号又得到了处理,connect调用还是会失败(errno被设置为EINTR),但连接尝试并不会被放弃,而是以异步方式继续建立,程序必须在此后进行检查以查看连接是否成功建立。
与accept调用一样,connect调用的阻塞特性可以通过设置该文件描述符的O_NONBLOCK标志来改变。此时,如果连接不能立刻建立,connect将失败并把errno设置为EINPROGRESS,而连接将以异步方式继续进行。
虽然异步连接难于处理,但我们可以在套接字文件描述符上用select调用来检查套接字是否已处于就绪状态。(此处先不将select调用)
你可以通过调用close函数来终止服务器和客户上的套接字连接,就如同对底层文件描述符进行关闭一样。你应该总是在连接的两端都关闭套接字。对于服务器来说,应该在read调用返回0时关闭套接字,但如果套接字是一个面向连接类型的,并且设置了SOCK_LINGER选项,close调用会在改套接字还有未传输数据时阻塞。(这里就涉及到如何设置套接字选项,这里不细讲)
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成
一段时间以来,我一直在使用open_uri下拉ftp路径作为数据源,但突然发现我几乎连续不断地收到“530抱歉,允许的最大客户端数(95)已经连接。”我不确定我的代码是否有问题,或者是否是其他人在访问服务器,不幸的是,我无法真正确定谁有问题。本质上,我正在读取FTPURI:defself.read_uri(uri)beginuri=open(uri).readuri=="Error"?nil:urirescueOpenURI::HTTPErrornilendend我猜我需要在这里添加一些额外的错误处理代码...我想确保我采取一切预防措施来关闭所有连接,这样我的连接就不是问题所在,但是我
我有一个super简单的脚本,它几乎包含了FayeWebSocketGitHub页面上用于处理关闭连接的内容:ws=Faye::WebSocket::Client.new(url,nil,:headers=>headers)ws.on:opendo|event|p[:open]#sendpingcommand#sendtestcommand#ws.send({command:'test'}.to_json)endws.on:messagedo|event|#hereistheentrypointfordatacomingfromtheserver.pJSON.parse(event.d
我选择的语言是Ruby,但因为Twitter,我知道Ruby不能处理很多请求。将它用于套接字开发是个好主意吗?或者我应该像Twitter开发人员那样使用像erlang或haskell或scala这样的函数式语言吗? 最佳答案 我工作的公司使用Ruby作为我们的网站。到目前为止,我们已经处理了超过34,000,000,000次点击。我们每天处理大约10,000,000次点击没有问题。每天的点击量峰值已超过40,000,000次。可扩展性取决于很多因素。例如,与读取相比,我们的数据库执行的写入比例高得不成比例。虽然大多数网站执行大约90
我有一个连接到服务器的rubytcpsocket客户端。在发送数据之前如何检查套接字是否已连接?我是否尝试“拯救”断开连接的tcpsocket,重新连接然后重新发送?如果是这样,有没有人有一个简单的代码示例,因为我不知道从哪里开始:(我很自豪我设法在rails中获得了一个持久连接的客户端tcpsocket。然后服务器决定杀死客户端,一切都崩溃了;)编辑我已经使用此代码解决了一些问题-如果未连接,它将尝试重新连接,但如果服务器已关闭则不会处理这种情况(它将继续重试)。这是正确方法的开始吗?谢谢defself.write(data)begin@@my_connection.write(
在Rails中,什么是集成更新模型某些元素的UDP监听过程的最佳方式(特别是它将向其中一个表添加行)。简单的答案似乎是在同一个进程中使用UDP套接字对象启动一个线程,但我什至不清楚我应该在哪里做适合Rails方式的事情。有没有一种巧妙的方法来开始收听UDP?具体来说,我希望能够编写一个UDPController并在每个数据报消息上调用一个特定的方法。理想情况下,我希望避免在UDP上使用HTTP(因为它会浪费一些在这种情况下非常宝贵的空间),但我完全控制消息格式,因此我可以为Rails提供它需要的任何信息。 最佳答案 Rails是一个
我正在构建一个连接到服务器并等待数据的客户端Ruby库,但也允许用户通过调用方法发送数据。我使用的机制是有一个初始化套接字对的类,如下所示:definitialize@pipe_r,@pipe_w=Socket.pair(:UNIX,:STREAM,0)end我允许开发人员调用以将数据发送到服务器的方法如下所示:defsend(data)@pipe_w.write(data)@pipe_w.flushend然后我在一个单独的线程中有一个循环,我从连接到服务器的socket和@pipe_r中选择:defsocket_loopThread.newdosocket=TCPSocket.new
我正在尝试使用Ruby创建套接字require"socket"w=UNIXSocket.new("socket")我不断遇到Nosuchfileordirectory-socket(Errno::ENOENT)这对我来说完全是倒退,因为new()应该创建那个丢失的文件。我错过了什么? 最佳答案 这太老了。请不要再尝试逐字使用它。http://blog.antarestrader.com/posts/153#!/rubyfile='path/to/my/socket'File.unlinkifFile.exists(file)&&Fi
恢复出厂设置后数据还在吗?格式化真的彻底吗?数据到底该怎么删?文章目录前言为什么不能彻底删除数据?手机如何彻底删除数据?第一步:恢复出厂设置第二步:手动覆写数据电脑如何彻底删除数据?方法一:命令提示符方法二:文件粉碎方法三:低级格式化硬盘总结前言个人数据安全再次成为大家关注的热门话题:在日常使用手机和电脑时,我们可能需要删除一些敏感数据,例如银行账户信息、个人照片、聊天记录等。数据删除的目的是保护个人隐私,避免泄露。以下是一些关于如何删除数据的方法。恢复出厂设置,不一定彻底清除手机数据电子产品迭代升级速度越来越快,大家家中的旧手机、旧电脑也越来越多,一键删除、格式化、恢复出厂设置能彻底清除个人