socket套接字介绍
本帖最后由 psych1 于 2019-11-13 11:33 编辑## 0x00 背景介绍
由于最近要完成自己的项目作业,其中需要使用socket进行网络通信。所以简单了解了一些这方面的知识,希望可以抛砖引玉。
## 0x01 socket介绍
socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。我们把插头插到插座上就能从电网获得电力供应,同样,为了与远程计算机进行数据传输,需要连接到因特网,而 socket 就是用来连接到因特网的工具。
套接字(socket)是一个抽象层,网络套接字是IP地址与端口的组合。学习 socket,也就是学习计算机之间如何通信,并编写出实用健壮的程序。
套接字有很多种类,而最具代表性的就是 Internet 套接字,我们常说的套接字,如果没有其他说明,都是指 Internet 套接字。
## 0x02 socket类型
根据数据的传输方式,可以将套接字分成两种类型。流格式套接字(SOCK_STREAM)和数据报格式套接字(SOCK_DGRAM)。通过 socket() 函数创建连接时,必须告诉它使用哪种数据传输方式。
流格式套接字是基于TCP协议,TCP 协议会控制你的数据按照顺序到达并且没有错误。所以说流格式套接字是一种可靠的、双向的通信数据流。
流格式套接字有以下特征:
> 数据在传输过程中不会消失;
数据是按照顺序传输的;
数据的发送和接收不是同步的
数据报格式套接字也是 IP 协议作路由,但是是依托UDP协议。数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字。当数据传输发生错误时,也不会重传。
数据报格式套接字有以下特征:
> 强调快速传输而非传输顺序;
传输的数据可能丢失也可能损毁;
限制每次传输的数据大小;
数据的发送和接收是同步的。
## 0x03 网络通信协议
socket是站在传输层的基础上,所以可以使用 TCP/UDP 协议,为了更好的了解socket,我们需要网络通信协议知识的储备,我把附件已经放在下面了,大家可以自取。至少要了解TCP/IP协议,关于TCP的三次握手(这也是面试常问的问题),
## 0x04 linux与windows系统下socket程序演示
linux系统下比较简单,我们实现一个简单的客户端从服务器读取“Hello World”并打印出来
服务器:
```cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(){
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//将套接字和IP、端口绑定
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));//每个字节都用0填充
serv_addr.sin_family = AF_INET;//使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//具体的IP地址
serv_addr.sin_port = htons(1234);//端口
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, 20);
//接收客户端请求
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
//向客户端发送数据
char str[] = "Hello World";
write(clnt_sock, str, sizeof(str));
//关闭套接字
close(clnt_sock);
close(serv_sock);
return 0;
}
```
客户端:
```cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main(){
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
//向服务器(特定的IP和端口)发起请求
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));//每个字节都用0填充
serv_addr.sin_family = AF_INET;//使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//具体的IP地址
serv_addr.sin_port = htons(1234);//端口
connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//读取服务器传回的数据
char buffer;
read(sock, buffer, sizeof(buffer)-1);
printf("Message form server: %s\n", buffer);
//关闭套接字
close(sock);
return 0;
}
```
windows系统下就比较麻烦了,需要预先加载Winsock.dll 或 ws2_32.dll文件,而且windows跟linux所用的一些函数功能虽然相同,但是名称却是不同的,这个要注意一下。
服务器:
```cpp
#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")//加载 ws2_32.dll
int main(){
//初始化 DLL
WSADATA wsaData;
WSAStartup( MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//绑定套接字
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));//每个字节都用0填充
sockAddr.sin_family = PF_INET;//使用IPv4地址
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");//具体的IP地址
sockAddr.sin_port = htons(1234);//端口
bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//进入监听状态
listen(servSock, 20);
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
//向客户端发送数据
char *str = "Hello World!";
send(clntSock, str, strlen(str)+sizeof(char), NULL);
//关闭套接字
closesocket(clntSock);
closesocket(servSock);
//终止 DLL 的使用
WSACleanup();
return 0;
}
```
客户端:
```cpp
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")//加载 ws2_32.dll
int main(){
//初始化DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//向服务器发起请求
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));//每个字节都用0填充
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(1234);
connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//接收服务器传回的数据
char szBuffer = {0};
recv(sock, szBuffer, MAXBYTE, NULL);
//输出接收到的数据
printf("Message form server: %s\n", szBuffer);
//关闭套接字
closesocket(sock);
//终止使用 DLL
WSACleanup();
system("pause");
return 0;
}
```
## 0x05 socket()函数解析
socket()函数在两个平台下参数是相同的,不同的是返回值,大家可以参考上面的代码。
先说一下linux系统下的socket()函数。在 Linux系统下使用 <sys/socket.h> 头文件中 socket() 函数来创建套接字,原型为:
> int socket(int af, int type, int protocol);
af指的是地址族(Address Family),也就是ip地址类型。当然有时候也会使用pf,到时候见到就不用惊讶了。type指的是数据传输方式/套接字类型,我们在上面已经讲了,根据数据传输方式的不同,我们可以将socket划分为两种常用类型,这里指的就是这个。protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,但是有时候有两种不同的协议支持同一种地址类型和数据传输类型,这个时候我们就需要指定传输协议了。
参数 af 的值为 PF_INET。如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP,因此可以这样来调用 socket() 函数:
> int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
这种套接字称为 TCP 套接字。
如果使用 SOCK_DGRAM 传输方式,那么满足这两个条件的协议只有 UDP,因此可以这样来调用 socket() 函数:
> int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
这种套接字称为 UDP 套接字。
上面两种情况都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议,如下所示:
> int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
>int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
windows平台下也是如此
> SOCKET socket(int af, int type, int protocol);
## 0x06 小结
希望大家多多支持。不要吝啬你们的评分呀,嘻嘻{:17_1060:} 滴滴滴!!!平民通道: 链接: https://pan.baidu.com/s/1CRtPoFx35oUAcSL0l5XKYQ 提取码: ma8s 复制这段内容后打开百度网盘手机App,操作更方便哦 楼主大神啊, 感谢楼主分享
楼主辛苦了 学习了,很受用,效果就相当于nc命令 还行不错不错 链接已经失效,有新的吗 求补连接
页:
[1]