谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注
没错,说的就是你,不用再怀疑!!!
希望我的文章内容能对你有帮助,一起努力吧!!!

1、Socket套接字
Socket 是一个编程接口(网络编程接口),是一种特殊的文件描述符( write/read )。 Socket 并不 仅限于 TCP/IP
Socket 独立于具体协议的编程接口,这个接口位于 TCP/IP 四层模型的应用层与传输层之间
1.1Socket的类型
- 流式套接字:( SOCK_STREAM ) 
  
- 面向字节流,针对于传输层协议为 TCP 协议的网络应用
 
 - 数据报套接字:( SOCK_DGRAM ) 
  
- 面向数据报,针对于传输层协议为 UDP 协议的网络应用
 
 - 原始套接字:( SOCK_RAW ) 
  
- 直接跳过传输层
 
 
1.2基于的TCP套件字编程流程
任何网络应用都会有通信双方:
- Send 发送端
 - recv 接收端
 
TCP 网络应用
- Client 客户端( TCP )
 - Server 服务端( TCP )
 
任何的网络应用:
- 传输层的协议(UDP/TCP)+端口号+IP地址
 
网络地址:
- 任何网络应用任意一方都需要有一个 网络地址 ( IP+端口 )
 
1.2.1TCP网络应用执行的大致过程
- 建立连接 
  
- 三次握手
 
 - 发送/接收数据 
  
- 发送数据:write/send/sendto
 - 接收数据:read/recv/recvfrom
 
 - 关闭连接 
  
- 四次挥手
 
 
1.3TCP网络应用的编程流程
1.3.1TCP-Server服务端
1)建立一个套件字:( socket )

2)绑定一个网络地址:( bind )
- 并不是任意的地址都可以(需要合法且能够正常访问)
 - 把一个套接字和一个网络地址进行绑定。如果想让其他人来主动联系/连接,就需要绑定一个 地址,并且需要把这个地址告诉其他人。不进行绑定,并代表套接字没有地址,不进行绑定套 接字在进行通信时候,内核会动态为套接字指定一个地址。
 

3)等待监听:( listen)
让一个套接字进入一个 监听状态

4)等待客户端的连接:( accept )
- 等待客户端来发起连接和客户端建立 TCP 连接 
  
- 三次握手
 
 - 函数成功返回表示和一个客户端完成连接
 - 多次调用函数就可以与不同的客户端进行连接
 

5)数据的传输: 读/写
发送数据: write/send/sendto
接收数据: read/recv/recvfrom

6)关闭套接字:( close/shutdown)
四次挥手

1.3.2TCP-Client客户端
- 建立一个套接字: socket
 - 绑定地址:可选/可以绑定也可也不绑定
 - 发起连接请求: connect 
  
- 主动的与 TCP-Server 建立连接。三次握手
 
 - 数据的传输:读/写 
  
- 发送数据:write/send/sendto
 - 接受数据:read/recv/recvfrom
 
 - 关闭套接字 
  
- close
 
 
1.4网络通信协议
协议:就是通信双方约定好的通信规则。

下面举例一个基础的C/S过程:
客户端:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
typedef enum
{
    CMD,
    MSG,
    IMG
}Data_t;
struct package
{
	// 数据类型
	Data_t DataType;
	
	// 数据实际的大小
	int DataSize;
	
	// 数据本体
	unsigned char Datas[0]; // 柔性数组
};
/*
    通过main函数的参数传递IP和端口
*/
int main(int argc,const char *argv[])
{
    // 申请套接字
    int sock_fd = socket(AF_INET,SOCK_STREAM,0);
    if(sock_fd == -1)
    {
        perror("申请失败");
        return -1;
    }
    std::cout << "套接字申请成功" << std::endl;
    std::cout << "客户端发起连接..." << std::endl;
    // 等待客户端连接
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(argv[1]);
    server.sin_port = atoi(argv[2]);
    
    if(connect(sock_fd,(struct sockaddr*)&server,sizeof(server) )== -1)
    {
        perror("连接失败");
        close(sock_fd);
        return -1;
    }
    std::cout << "客户端连接成功" << std::endl;
    // 数据传输
    while(1)
    {
        std::string str;
        std::cout << "输入消息:";
        std::cin >> str;
        // 应用层封包过程
        struct package *data = (struct package*)new char[sizeof(struct package)+str.size()];
        data->DataType = MSG;
        data->DataSize = str.size();
        strcpy((char *)data->Datas,str.c_str());
        int ret = write(sock_fd,data,sizeof(struct package)+str.size());
        if(ret == -1||str == "exit")
            break;
    }
    // 关闭套接字
    close(sock_fd);
    return 0;
} 
服务端:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
typedef enum
{
    CMD,
    MSG,
    IMG
}Data_t;
struct package
{
	// 数据类型
	Data_t DataType;
	
	// 数据实际的大小
	int DataSize;
	
	// 数据本体
	unsigned char Datas[0]; // 柔性数组
};
/*
    通过main函数的参数传递IP和端口
*/
int main(int argc,const char *argv[])
{
    // 申请套接字
    int sock_fd = socket(AF_INET,SOCK_STREAM,0);
    if(sock_fd == -1)
    {
        perror("申请失败");
        return -1;
    }
    std::cout << "套接字申请成功" << std::endl;
    // 绑定网络地址
    struct sockaddr_in local; // 绑定本机的网络地址
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = inet_addr(argv[1]); // 将字符串的ip地址转化为网络字节序列
    local.sin_port = atoi(argv[2]);
    
    if(bind(sock_fd,(struct sockaddr*)&local,sizeof(local)) == -1)
    {
        perror("绑定失败");
        close(sock_fd);
        return -1;
    }
    std::cout << "套接字绑定成功" << std::endl;
    // 进入监听状态
    if(listen(sock_fd,10) == -1)
    {
        perror("监听失败");
        close(sock_fd);
        return -1;
    }
    std::cout << "套接字监听成功" << std::endl;
    std::cout << "等待客户端连接..." << std::endl;
    // 等待客户端连接
    struct sockaddr_in client;
    socklen_t client_size = sizeof(client);
    int newSock = accept(sock_fd,(struct sockaddr*)&client,&client_size);
    // 判断客户端是否连接成功
    if(newSock == -1)
    {
        perror("客户端连接失败");
        close(sock_fd);
        return -1;
    }
    std::cout << "客户端连接成功" << std::endl;
    // 数据传输
    while(1)
    {
        struct package *data = (struct package*)malloc(sizeof(struct package));
        // 获取第一次数据
        int ret = read(newSock,data,sizeof(struct package));
        if(ret == -1)
            break;
        std::cout << "本次数据包的大小:" << data->DataSize << std::endl;
        // 重新为结构体分配空间
        data = (struct package*)realloc(data,sizeof(struct package)+data->DataSize);
        // 获取第二次数据
        ret = read(newSock,data->Datas,data->DataSize);
        if(ret == -1 || std::string((char *)data->Datas) == "exit")
            break;
        std::cout << "来自服务端的消息<" << inet_ntoa(client.sin_addr) << ">" << data->Datas << std::endl;
        free(data);
    }
    // 关闭套接字
    close(sock_fd);
    return 0;
} 
1.5UDP套接字编程
UDP 传输层的协议,面向无连接,数据报的传输层协议。
“ 无连接 ”:不可靠
- 在网络环境较好的情况下, UDP 效率较高
 - 在网络环境较差的情况下, UDP 可能存在丢包的情况
 - 同时一些“ 实时应用 ” 采用 UDP
 - 在应用层加一些保证传输可靠的“ 控制协议 ”
 
1.5.1UDP Server(接收端) :
- 创建一个套接字
 - 绑定网络地址
 - 数据通信
 
- 关闭套接字
 
1.5.2UDP Client(发送端) :
- 创建一个套接字
 
- 关闭套接字
 
下面是有关UDP的C/S过程基础示例:
服务端:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
int main()
{
    int sock_id = socket(AF_INET,SOCK_DGRAM,0);
    if(sock_id == -1)
    {
        perror("套接字申请失败");
        return -1;
    }
    while(1)
    {
        char buf[100]={0};
        std::cout << "请输入发送的消息:";
        std::cin >> buf;
        // 指定目标地址
        struct sockaddr_in revcAddress;
        revcAddress.sin_family = AF_INET;
        revcAddress.sin_addr.s_addr = inet_addr("192.168.8.130");
        revcAddress.sin_port = 8899;
        if(sendto(sock_id,buf,strlen(buf),0,(struct sockaddr*)&revcAddress,sizeof(revcAddress)) == -1||std::string(buf) == "exit")
            break;
    }
    return 0;
} 
客户端:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
int main()
{
    int sock_id = socket(AF_INET,SOCK_DGRAM,0);
    if(sock_id == -1)
    {
        perror("套接字申请失败");
        return -1;
    }
    // 进行地址绑定,绑定了你才可以接收消息
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port   = 8899;
    local.sin_addr.s_addr = inet_addr("192.168.8.130");
    if(bind(sock_id,(struct sockaddr*)&local,sizeof(local)) == -1)
    {
        perror("套接字申请失败");
        close(sock_id);
        return -1;
    }
    while(1)
    {
        char buf[100]={0};
        // 指定目标地址
        struct sockaddr_in sendAddress;
        socklen_t size = sizeof(sendAddress);
        if(recvfrom(sock_id,buf,1000,0,(struct sockaddr*)&sendAddress,&size) == -1||std::string(buf) == "exit")
            break;
        std::cout << "来自于<"<< inet_ntoa(sendAddress.sin_addr) <<"> : " << buf << std::endl;
    }
    return 0;
} 
2、设置套接字
每一个套接字都有一些不同行为和属性:
- 如:每个套接字在内核中,都会有两个缓冲区 
  
- send buffer
 - recv buffer
 
 - 每个缓冲区的大小是多少?
 - 缓冲区可以设置其大小的。
 
2.1获取套接字选项

2.2设置套接字选项

2.3套接字选项表




















