目录
为什么要称socket为套接字?首先套接字的原词为"socket",直译过来就是插座的意思,最先采用这个词的人,觉得网络连接,就像插口和插座一样,一方插,一方被插(知乎用户回答)
除此之外,linux等系统中“套接字”对应“socket word”,所以“字”也就是对应“word”,可能指计算机数据,也可能指存储socket的数据表示,因为端口号是两个字节,就是一个WORD。
至于为什么翻译为“套接字”:有人说是“套用-接口-标识”的意思;有人说是“套接起来的字符串”的意思;有人说“是将网络数据包一层一层地套起来传输”的意思。
总之就是望文生义,毕竟怎么解释都不重要,东西还是那个东西
看了一圈,最比较赞同的解释是:套接指的是套接管,就是奖两根水管套接起来的关资,然后“字”就是连接数据的标识符,所以套接字就是标识连接的数据体。(附作者链接Socket为什么要翻译成套接字? - FrankIsFree的回答 - 知乎


首先创建一个空的控制台应用,按照TCP实现流程编写代码。
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1); //MAKEWORD(a,b) b|a<<8
err = WSAStartup(wVersion, &wsaData);
//检查1
if (err != 0) {
return err;
}
//检查2
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
//清理套接字库
WSACleanup();
return -1;
}
当我们再进行socket编程时,要调用各种socket函数,而且还需要用到一个库文件Ws2_32.lib个一个头文件Winsock2.h
wsastartup()函数向操作系统说明,我们要用哪个库文件。 因此就可以将库文件与当前的应用程序绑定,从而就可以调用该版本的socket的各种函数了。 一句话解释:wsastartup()主要就是进行相应的socket库绑定。
WSAStarup
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
WSAStartup需要传入两个参数,wVersionRequested指明需要库的版本号,lpWSAData为指向WSAData数据结构的指针,用来接收Windows Sockets实现细节(存储初始化数据)
调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。 否则返回对应错误的宏。
WORD是微软SDK中的类型,意为'字' ,是2byte无符号整数,表示范围0~65535相当于C语言中的2个char
MAKEWORD();可以创建WORD类型。其工作原理类似于bLow | bHigh<<8
WORD MAKEWORD( //函数原型 BYTE bLow, //指定新变量的低字节序; BYTE bHigh //指定新变量的高字节序; );在进行初始化结果判断时需判断两个:第一个是WSAStarup函数的返回值,判断返回结果是否为0;第二个是存储初始化数据WSAData的高位和地位是否和我们指定的库版本一致。
//检查1 if (err != 0) { return err; } //检查2 if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { //清理套接字库 WSACleanup(); return -1; }
- WSACleanup()函数
int WSACleanup (void);
应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
//创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
socket函数用于创建一个socket描述符,它唯一标识一个socket,包含了三个参数:
int socket(int domain, int type, int protocol);
协议族决定了socket的地址类型,在通信中必须采用相应的地址.

函数socket()的参数type用于设置socket通信类型

并不是所有的协议族都实现了这些协议类型,例如,AF_INET协议族就没有实现SOCK_SEQPACKET协议类型。
类型为SOCK_STREAM的套接字表示一个双向的字节流,与管道类似。流式的套接字在进行数据收发之前必须已经连接,连接使用connect()函数进行。一旦连接,可以使用read()或者write()函数进行数据的传输。流式通信方式保证数据不会丢失或者重复接收,当数据在一段时间内任然没有接受完毕,可以将这个连接人为已经死掉。
SOCK_DGRAM和SOCK_RAW 这个两种套接字可以使用函数sendto()来发送数据,使用recvfrom()函数接受数据,recvfrom()接受来自制定IP地址的发送方的数据。
SOCK_PACKET是一种专用的数据包,它直接从设备驱动接受数据。
函数socket()的第3个参数protocol用于制定某个协议的特定类型,即type类型中的某个类型。通常某协议中只有一种特定类型,这样protocol参数仅能设置为0;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。
1.首先准本绑定信息。
在准本绑定信息中既要指明绑定的IP地址,同时也要指明绑定的端口号
SOCKADDR_IN addrSrv;
//地址
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//协议族,与上面保持一致
addrSrv.sin_family = AF_INET;
//端口;0~65535,其中1024以下的端口为系统保留的
addrSrv.sin_port = htons(6000);
sockaddr 和 sockaddr_in 这两个结构体用来处理网络通信的地址。
sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了;sockaddr_in该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
sockaddr和sockaddr_in二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。
sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。
htonl函数
将主机的unsigned long值转换成网络字节顺序。主要针对32位的(long)
htons函数
htons()作用是将端口号由主机字节序转换为网络字节序的整数值。主要针对16位的(short)
htonl和htons函数
之所以要进行字节序的转换,是因为主机字节序和网络字节序的存储不同:
主机字节序:
1)大端存储:低位字节放在内存的高地址端,高位字节放在内存的低地址端
2)小端存储:低位字节放在内存的低地址端,高位字节放在内存的高地址端网络字节序:
UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在低地址处);由此可见,多字节数值在发送之前,在内存中因该是以大端存储。而一般x86计算机用的是小端存储~
在一些socket通信的服务器程序中我们会看到在服务器bind IP地址和端口号时,我们不是bind明确的IP地址(如222.20.79.150),而是使用INADDR_ANY。
INARRD_ANY是用于多网卡的机器上的,多网卡就会有多个IP地址。比如你的机器有3个IP:192.168.1.1、202.202.202.202和61.1.2.3。如果设置serv.sin_addr.s_addr=inet_addr("192.168.1.1");然后监听100端口,这时其他机器只有连接到192.168.1.1才能成功;连接202.202.202.202:100或61.1.2.3:100都会失败。如果设置serv.sin_addr.s_addr=htonl(INADDR_ANY);的话,无论连接哪个IP都可以连接上。
总的来说INADDR_ANY参数就表明可以连接到本机的所有ip都是可以的,极大的简化了需要创建socket的数量,因为我们就绑定一个INADDR_ANY和一个端口,然后客户端通信到这个机器的所有ip都用这个socket来处理。
2.绑定信息准备好后则进行绑定到本机
//绑定
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
bind()函数原型
int bind (int sockfd, const struct sockaddr * addr, socklen_t addrlen);
1.sockfd:即为socket描述字,他是通过socket()函数创建的,唯一标识一个socket。bind函数就是将这个描述子绑定一个名字
2.addr:一个sockaddr*指针,指向地址结构的指针,根据创建socket时的地址协议不同而不同。
3.addrlen:对应地址结构的长度。通常服务器在启动时会绑定一个众所周知的地址(ip地址+端口号)。而客户端不用指定系统自动分配,所以通常服务端在listen之前要调用bind(),而客户端不会调用,在connect()时由系统随机生成一个。
bind函数运行成功后会将端口和IP地址绑定到Socket描述符上,返回0时表示绑定成功。
//监听
listen(sockSrv, 10);
listen()函数原型
int listen(int sockfd, int backlog)
listen函数的第一个参数时即将要监听的socket描述字,第二个参数为相应的socket可以排队的最大连接数。socket()创建的socket默认是一个主动类型,listen则将socket变成被动类型,等待客户连接请求。
对于调用listen进行监听的套接字,操作系统会为其维护2个队列:未完成队列,已完成队列。
listen的第二参数backlog则表示,这两个队列的和不能超过baklog。
这个过程就如下图所示:


//接收请求前的准备工作
SOCKADDR addrCli;
int len = sizeof(SOCKADDR);
while (true) {
//接收链接请求,返回针客户端的套接字
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
//关闭连接
closesocket(sockConn);
}
accept()函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回客户端套接字的标识,一个客户端的socket
注意:如果没有客户端套接字去请求,它便会在那里一直等下去。如果是非阻塞式的socket, 那么accept函数会立即返回。
char recvBuf[100];
char sendBuf[100];
while (true) {
//接收连接请求,返回针对客户端的套接字
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
//准备发送的数据
sprintf_s(sendBuf, 100, "hello world");
//发送数据
send(sockConn, sendBuf, sizeof(sendBuf) + 1, 0);
//接收数据
recv(sockConn, recvBuf, 100, 0);
std::cout << recvBuf << std::endl;
//关闭套接字
closesocket(sockConn);
}
int send( SOCKET s, const char FAR *buf, int len, int flags );
int recv( SOCKET s, char FAR *buf, int len, int flags );
#include <iostream>
#include<WinSock2.h>//第二版本的网络库
#pragma comment(lib,"ws2_32.lib")
//一般模板
/*
* 0.初始化套接字库
* 1.创建socket
* 2.绑定到本机
* 3.开始监听
* while(true){
* 4.接收客户端连接
*
* 5.关闭客户端socket
* }
*
* 6.关闭服socket
* 7.清理套接字库
*/
int main() {
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1); //MAKEWORD(a,b) b|a<<8
err = WSAStartup(wVersion, &wsaData);
//检查1
if (err != 0) {
return err;
}
//检查2
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
//清理套接字库
WSACleanup();
return -1;
}
//创建tcp套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
//绑定到本机
//绑定即要指明绑定的哪个IP地址,同时指明绑定的端口号
//准备绑定信息
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//协议族,与上面保持一致
addrSrv.sin_family = AF_INET;
//端口;0~65535,其中1024以下的端口为系统保留的
addrSrv.sin_port = htons(6000);
//绑定
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//监听
listen(sockSrv, 10);
std::cout << "Server start at 6000" << std::endl;
//接收请求前的准备工作
SOCKADDR addrCli;
int len = sizeof(SOCKADDR);
char recvBuf[100];
char sendBuf[100];
while (true) {
//接收链接请求,返回针对客户端的套接字
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
//准备发送的数据
sprintf_s(sendBuf, 100, "hello world");
//发送数据
send(sockConn, sendBuf, sizeof(sendBuf) + 1, 0);
//接收数据
recv(sockConn, recvBuf, 100, 0);
std::cout << recvBuf << std::endl;
//关闭套接字
closesocket(sockConn);
}
//关闭套接字
closesocket(sockSrv);
//清理套接字库
WSACleanup();
system("pause");
}
首先在同一解决方案下另建一个控制台应用,接着按照TCP客户端的实现流程编写代码

客户端初始化套接字库的代码与服务端的基本上是一致的,要注意客户端和服务端的套接字库版本相一致。
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1); //MAKEWORD(a,b) b|a<<8
err = WSAStartup(wVersion, &wsaData);
//检查1
if (err != 0) {
return err;
}
//检查2
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
//清理套接字库
WSACleanup();
return -1;
}
//创建tcp套接字,与服务器保持一致
SOCKET sockCli= socket(AF_INET, SOCK_STREAM, 0);
//绑定到本机
//绑定即要指明绑定的哪个IP地址,同时指明绑定的端口号
//准备绑定信息
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//协议族,与上面保持一致
addrSrv.sin_family = AF_INET;
//端口;0~65535,其中1024以下的端口为系统保留的
addrSrv.sin_port = htons(6000);
客户端创建套接字要指明绑定的IP地址和端口。
iner_addr会检查传入的字符串是否是一个合法的IP地址(例如格式或者每个字段是否<255) ,如果非法则返回INADDR_NONE,正确执行将返回一个无符号长整数型数
connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
connect()用于建立与指定socket的连接。
函数原型
int connect(SOCKET s, const struct sockaddr * name, int namelen);
connect操作之后代表对应的套接字已连接 ,发送与接收数据时就可以使用这个套接字。
//发送的数据
char sendBuf[] = "world";
//接收的数据
char recvBuf[100];
//发送数据到服务器
send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
//接收数据到服务器
recv(sockCli, recvBuf, sizeof(recvBuf), 0);
注意程序结束前要关闭套接字,清理套接字库。
//关闭套接字
closesocket(sockCli);
WSACleanup();
#include <iostream>
#include<WinSock2.h>//第二版本的网络库
#pragma comment(lib,"ws2_32.lib")
#include <iostream>
int main() {
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1); //MAKEWORD(a,b) b|a<<8
err = WSAStartup(wVersion, &wsaData);
//检查1
if (err != 0) {
return err;
}
//检查2
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
//清理套接字库
WSACleanup();
return -1;
}
//创建tcp套接字,与服务器保持一致
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
//准备连接信息
//指明要连接的IP地址和端口号
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//协议族,与上面保持一致
addrSrv.sin_family = AF_INET;
//端口;0~65535,其中1024以下的端口为系统保留的
addrSrv.sin_port = htons(6000);
//连接服务器
connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//发送的数据
char sendBuf[] = "world";
//接收的数据
char recvBuf[100];
//发送数据到服务器
send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
//接收数据到服务器
recv(sockCli, recvBuf, sizeof(recvBuf), 0);
std::cout << recvBuf << std::endl;
//关闭套接字
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
}
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
网络编程套接字网络编程基础知识理解源`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()还是其他方法完成
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
一段时间以来,我一直在使用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
我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。
我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一
使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案
我正在开发一个xcode自动构建系统。在执行一些预构建验证时,我想检查指定的证书文件是否已被撤销。我了解securityverify-cert验证其他证书属性但不验证吊销。我如何检查撤销?我正在用Ruby编写构建系统,但我对任何语言的想法都持开放态度。我阅读了这个答案(Openssl-Howtocheckifacertificateisrevokedornot),但指向底部的链接(DoesOpenSSLautomaticallyhandleCRLs(CertificateRevocationLists)now?)进入的Material对我的目的来说有点过于复杂(用户上传已撤销的证书是一