socket编程—UDP套接字
一、UDP套接字
IP是标识互联网当中的唯一一台主机
端口号是标识一台主机内的唯一一个进程
两者相加就是标识互联网当中唯一一个进程
整个互联网可以看作一个大的OS,所有在网络上的行为,基本都是在一个大的OS内,进行进程间通信!
所以后续我们IP+端口号=socket
要进行通信,本质:
1、先找到目标主机
2、在找到该主机上的服务(进程)
互联网世界,是一个进程间通信的世界
进程具有独立性,进程间通信的前提工作:先让不同的进程,看到同一份资源。这里的同一份资源是网络!
socket函数的参数
套接字类型:用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。
struct sockaddr_in结构体
socket()函数返回值
返回一个文件描述符
1、服务端
创建套接字
作为一个服务器,要不要让客户知道,对应的服务器的地址(ip+port)?必须知道!
服务器的socket信息(ip+port),必须得让客户知道!
一般的服务器port,必须是众所周知(不仅是被人,也可以被各种软件等知道的)的,并且轻易不改变!
绑定端口
bind()函数
提供服务
recvfrom()函数接收对端发来的数据
返回值:
sendto()返回给对端数据
server代码:
#include<iostream>
#include<string>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
//const uint16_t port = 8081;
void Usage(std::string proc)
{
std::cout<<"Usage:"<<proc<<"proc"<<std::endl;
}
int main(int argc,char *argv[])
{
if(argc != 2){
Usage(argv[0]);
return -1;
}
uint16_t port = atoi(argv[1]);
//AF_INET 表示 IPv4 地址 OCK_DGRAM(数据报套接字/无连接的套接字)
int sock = socket(AF_INET,SOCK_DGRAM,0);//1、创建套接字
if(sock < 0)//如果创建失败
{
std::cerr << "socket create error:" << errno <<std::endl;
return 1;
}
//2、给该服务器绑定端口和ip(需要特殊处理,因为大小端序不一样)
struct sockaddr_in local;//创建一个struct sockaddr_in结构的对象
//开始填充该对象的字段
local.sin_family = AF_INET;
local.sin_port = htons(port);//此处的端口号,是我们计算机上的变量,是主机序列,可能需要主机转网络:htons函数
//a、需要将人识别的点分十进制,字符串风格IP地址,转化为4字节整数IP
//b、也考虑大小端
//in_addr_t inet_addr(const char *cp) ;这个函数能完成ab两个工作
//坑:
//云服务器:不允许用户直接bind公网IP,另外,实际正常编写的时候,我们也不会指明IP
//local.sin_addr.s_addr = inet_addr("175.178.47.247");//"175.178.47.247"为点分十进制,字符串风格
//INADDR_ANY:如果你bind的是明确的IP(主机),意味只有发到该ip主机上的数据
//才会交给你的网络进程,但是,一般服务器可能有多张网卡,配置多个ip,我们需要的不是
//某个ip上面的数据,我们需要的是,所有发送到该主机,发送到该端口的数据!
local.sin_addr.s_addr = INADDR_ANY;//INADDR_ANY零值,相当于默认
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){//(struct sockaddr*)&local需要强转
std::cerr << "bind error :" <<errno << std::endl;
}
//3、提供服务
bool quit = false;
#define NUM 1024
char buffer[NUM];//接收数据缓冲区
while(!quit)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//注意:我们默认认为通信的数据是双方在互发字符串
ssize_t cnt = recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//接收客户端发来的数据,并确认对端是谁
if(cnt > 0){
buffer[cnt] = 0;
std::cout<<"client# " << buffer <<std::endl;
//根据用户输入,构建一个新的返回字符串
std::string echo_hello = buffer;
echo_hello += "....";
sendto(sock,echo_hello.c_str(),echo_hello.size(),0,(struct sockaddr*)&peer,len);//返回(发送)数据给对端服务器
}
else{
}
}
return 0;
}
2、客户端
创建套接字
//客户端需要显示的bind嘛?不需要
//a、首先,客户端必须也要有ip和port
//b、但是,客户端不需要显示的bind!一旦显示bind,就必须明确,client要和哪一个port关联
//client指明的端口号,在client端一定有嘛?有可能被占用,被占用导致client无法使用
//server要的是port必须明确,而且不变,但是client只要有就行!一般是由OS自动给你bind()
//就是client正常发送数据的时候,OS会自动给你bind,采用的是随机端口的方式!
client端代码:
#include<iostream>
#include<string>
#include<cerrno>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void Usage(std::string proc)
{
std::cout<<"Usage: \n\t" <<proc<<"server_ip server_port"<<std::endl;
}
int main(int argc,char *argv[])
{
if(argc != 3){
Usage(argv[0]);
return 0;
}
//1、创建套接字。打开网咯文件
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0){
std::cerr<<"socket error" <<errno<<std::endl;
return 1;
}
//客户端需要显示的bind嘛?不需要
//a、首先,客户端必须也要有ip和port
//b、但是,客户端不需要显示的bind!一旦显示bind,就必须明确,client要和哪一个port关联
//client指明的端口号,在client端一定有嘛?有可能被占用,被占用导致client无法使用
//server要的是port必须明确,而且不变,但是client只要有就行!一般是由OS自动给你bind()
//就是client正常发送数据的时候,OS会自动给你bind,采用的是随机端口的方式!
struct sockaddr_in server;//填充服务端数据
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));//存储端口号
server.sin_addr.s_addr = inet_addr(argv[1]);//存储IP地址
//2、使用服务
while(1){
//a、你的数据从哪里来?
std::string message;
std::cout<<"输入#";
std::cin >> message;
//b.你要发给谁?
sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));//发送数据给对端
//此处tmp就是一个“占位符”
struct sockaddr_in tmp;
socklen_t len = sizeof(tmp);
char buffer[1024];
ssize_t cnt = recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&tmp,&len);
if(cnt > 0){
//在网络通信中,只有报文大小,或者是字节流中字节的个数,没有C/C++字符串这样的概念
buffer[cnt] = 0;
std::cout<<"server# " << buffer <<std::endl;
}
else{
}
}
return 0;
}
注:一些函数的解析为网上截图,如有侵权请联系删除;