目录
前言
提示:这里可以添加本文要记录的大概内容:
socket编程又称套接字编程,指进行网络通信程序的编写
提示:以下是本篇文章正文内容,下面案例可供参考
一、socket
网络通信就是将原始数据进行tcp/ip四层封装,通过网卡发送实现通信
网络通信分为:客户端、服务端。
客户端:在客户这一端的进程,是主动发送请求的一端。客户端必须提前知道服务端的
地址与端口才能给服务端发送请求。
服务端:提供服务的一端进程,是被动接收请求从而进行处理的一端。
二、socket接口
1.UDP通信流程原理
sockaddr 是一个通用的套接字地址结构体,包含了一个地址族和地址数据,主要用来存储和传递套接字地址信息的。
struct sockaddr { unsigned short sa_family; // 地址族 char sa_data[14]; // 地址信息 };
字节序转换接口---将主机字节序转化为网络字节序,字节序是存储多字节数据的
uint16_t htons(uint16_val); uint16_t ntons(uint16_val); --- 返回uint16_t数据
uint32_t htonl(uint32_t val) uint32_t ntonl(uint32_t val) --- 返回uint32_t数据
所以2字节只能使用s进行转换,4字节只能属于l,这是指定的不能使用其他的
in_addr_t inet_addr(const char* ip) //将字符串IP地址转换为网络字节序整形IP地址
const char* inet_ntoa(struct in_addr_t inet_addr;) //将网络字节序整形IP地址转换
为字符串IP地址
socket接口:
int socket(int domain, int type, int protocol); //创建socket,返回一个socket描述符
domain:指定了套接字使用的协议 AF_INET:用IPv4网络协议
AF_INET6:用IPv6网络协议 AF_UNIX或AF_LOCAL:本地进程间通信
type:指定套接字的类型
SOCK_STREAM:字节流传输通常用于TCP
SOCK_DGRAM:数据报传输通常用于UDP
protocol:协议类型:通常设置为0
IPPROTO_TCP:TCP协议
IPPROTO_UDP:UDP协议
返回值:失败返回-1
绑定地址接口
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen) //为创建的套接字绑定一个
地址
sockfd:创建套接字返回的描述符
addr:指向sockaddr结构体指针,包含要绑定的地址信息
addrlen:地址长度,取决于IP协议类型
返回值:成功返回0,失败返回-1
发送数据接口:
ssize_t sendto(
int sockfd, //返回的套接字描述符
void *buf, //要发送数据的缓冲区指针
size_t len,, //要发送的数据的长度--字节
int flags, //通常设置为0-阻塞发送, MSG_DONTWAIT--非阻塞
struct sockaddr *_addr, //一个指向sockaddr结构体指针,实际
使用中通常会使用 sockaddr_in(对于
IPv4)或 sockaddr_in6(对于IPv6)-
目的端地址信息
socklen_t addrlen 地址长度
)
返回值:成功返回实际发送的数据长度,失败返回-1
接收数据接口
ssize_t recvfrom(
int sockfd,
void *buf //存放接收数据的缓冲区指针--空间地址
size_t len //要接收数据的长度
int flags, //
struct sockaddr *dest_addr, //对端地址信息
socklen_t addrlen //用于设定想要获取的地址长度
)
返回值:成功返回实际接收到的数据长度,失败返回-1
关闭套接字
int close(int sockfd);
UDP协议:无连接、不可靠、面向数据报
无连接:只要知道对方的地址就可以给对方发送数据
不可靠:数据没有丢包检测,丢了就没了,且不保证有序
面向数据包:传输以快为单位,有最大数据传输限制
其传输性能较高,多用于视频、音频等资源传输
2.代码
服务端
c
// udp服务端程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>//htons字节序转换接口
#include <netinet/in.h>
#include <sys/socket.h>
#include <error.h>
int main(int argc, char *argv[]){
//argc表示命令行参数个数。如果你在命令行中运行程序 ./myprogram arg1 arg2,那么 argc 的值将为3("./myprogram", arg1, arg2)
// ./udp_ 192.168.x.x 9000
if(argc != 3){
perror("./udp_ 172.23.62.176 9000");
}
char *ip_ = argv[1]; //argv[0]是程序的名称,argv[1]是第一个参数ip地址
uint16_t port_ = atoi(argv[2]); //将字符串转化整数
//创建套接字
int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socketfd < 0){
perror("socket error");
return -1;
}
//绑定地址
struct sockaddr_in addr; //IPv4的结构体
//配置结构体
addr.sin_family = AF_INET;//地址族
addr.sin_port = htons(port_); //导入ip地址
addr.sin_addr.s_addr = inet_addr(ip_);
socklen_t addrlen = sizeof(struct sockaddr_in); //计算接收的地址长度
int bind_ = bind(socketfd, (struct sockaddr*) &addr, addrlen);
if(bind_ < 0){
perror("bind error");
return -1;
}
while(1){
//接收数据
char buf[1024] = {0};
struct sockaddr_in cliaddr;
socklen_t addrlen = sizeof(struct sockaddr_in);
ssize_t recv = recvfrom(socketfd, buf, 1023, 0, (struct sockaddr*)&cliaddr, &addrlen);
if(recv < 0){
perror("recvfrom error");
return -1;
}
uint16_t cliport = ntohs(cliaddr.sin_port);
const char *cliip = inet_ntoa(cliaddr.sin_addr);
printf("client[%s:%d] say: %s\n",cliip, cliport, buf);
//发送数据
printf("server say:");
fflush(stdout);
fgets(buf, 1023, stdin);//从键盘获取数据
int send = sendto(socketfd, buf, strlen(buf), 0, (struct sockaddr*)&cliaddr, addrlen);
if(send < 0){
perror("sendto error");
return -1;
}
}
//关闭套接字
close(socketfd);
return 0;
}
客户端
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <error.h>
#define CHECK_RET(r) if((r) == false){return -1;}
class UdpSocket {
private:
int _socketfd; //定义一个套接字
public:
UdpSocket() :_socketfd(-1) {}
~UdpSocket() { Close(); }
bool Socket() {//创建套接字
_socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(_socketfd < 0){
perror("Socket error");
return false;
}
return true;
}
bool Bind(const std::string &ip, uint16_t port){ //绑定地址信息
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t addrlen = sizeof(struct sockaddr_in);
int bend_ = bind(_socketfd, (struct sockaddr*)&addr, addrlen);
if(bend_ < 0){
perror("Bind error");
return false;
}
return true;
}
bool Send(const std::string &body, const std::string& ip, uint16_t port){ //发送数据
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t addrlen = sizeof(struct sockaddr_in);
ssize_t send = sendto(_socketfd, body.c_str(), body.size(), 0, (struct sockaddr*)&addr, addrlen);
if(send < 0){
perror("Send error");
return false;
}
return true;
}
bool Recv(std::string* body, std::string *ip = NULL, uint16_t *port = NULL){ //接收数据
struct sockaddr_in addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
char tmp[4096] = {0};
ssize_t recv = recvfrom(_socketfd, tmp, 4096, 0, (struct sockaddr*)&addr, &addrlen);
if(recv < 0){
perror("Recv error");
return false;
}
if(ip != NULL) *ip = inet_ntoa(addr.sin_addr);
if(port != NULL) *port = ntohs(addr.sin_port);
body->assign(tmp, recv);
return true;
}
bool Close(){ //关闭套接字
if(_socketfd < 0){
return true;
}
close(_socketfd);
_socketfd = -1;
return true;
}
};
int main(int argc, char* argv[])
{
if(argc != 3){
std::cout <<"./udp 172.23.62.176 9000\n";
return -1;
}
UdpSocket us; //实例化对象
//创建套接字
CHECK_RET(us.Socket());
while(1){
std::string buf;
std::cout <<"client say:";
fflush(stdout); //刷新输出缓冲区
std::cin >> buf;//向缓冲区写入数据
//发送数据
CHECK_RET(us.Send(buf, argv[1], std::stoi(argv[2])));
//接收数据
buf.clear(); //清空缓冲区
CHECK_RET(us.Recv(&buf));
std::cout << "server say:" << buf << std::endl;
}
//关闭套接字
us.Close();
return 0;
}