谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注
没错,说的就是你,不用再怀疑!!!
希望我的文章内容能对你有帮助,一起努力吧!!!
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
- 每个缓冲区的大小是多少?
- 缓冲区可以设置其大小的。