psych1 发表于 2019-11-13 11:21

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:}

psych1 发表于 2019-11-13 11:22

滴滴滴!!!平民通道: 链接: https://pan.baidu.com/s/1CRtPoFx35oUAcSL0l5XKYQ 提取码: ma8s 复制这段内容后打开百度网盘手机App,操作更方便哦

pojiedaxia 发表于 2019-11-13 11:48

楼主大神啊,

fire0416 发表于 2019-11-13 13:20

感谢楼主分享
楼主辛苦了

ylrenxd 发表于 2019-11-15 12:48

学习了,很受用,效果就相当于nc命令

vevei_ps 发表于 2019-11-16 13:00

还行不错不错

hanSenWA 发表于 2020-9-8 17:27

链接已经失效,有新的吗

Geekerleon 发表于 2020-9-8 17:32

求补连接
页: [1]
查看完整版本: socket套接字介绍