套接字:操作系统向上层提供的用于实现网络通信的统称
网络通信其实本质上就是两台主机之间的通信其中一段是客户端,另一端是服务器
客户端:用户的一端,客户端是主动发出请求的一端
服务端:针对用户请求提供服务的一端,服务器是被动接收的一端
传输层协议:TCP UDP
TCP :传输控制协议,面向连接,可靠传输,面向字节流
应用于安全性要求大于实时性要求的场景 例如文件传输
UDP:用户数据包协议,无连接,不可靠,面向数据报
应用于对实时性要求大于安全性要求的场景 例如视频音频
UDP通信程序编写:
客户端:
1.创建套接字
2.为套接字绑定地址信息
客户端不推荐绑定指定地址
1.绑定之后,程序只能在启动一个
2.客户端并不需要固定使用某个地址
3.向服务器发送数据
发送数据之前若socket没有绑定指定的地址信息,系统会选择合适的地址信息进行绑定
4.接受数据
5.关闭套接字
服务器
1.创建套接字
在内核创建socket 结构体,将进程跟网卡关联起来
2.为套接字绑定指定地址信息
给创建套接字socket结构体描述源端地址信息
1.告诉系统,网卡收到哪个数据应该交给我
2.当发送数据的时候使用绑定的地址信息作为源端地址信息
3.接受数据
从socket的接受缓冲区取出数据
4.发送数据
把要发送的数据放到发送缓冲区
5.关闭套接字
UDP要用到的套接字接口介绍
socket();
函数原型:int socket(int domain, int type, int protocol);
参数介绍:
domain:地址域类型 ,指明使用的协议族。常用的协议族有AF_INET、AF_INET6,协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合
type:指明socket类型 SOCK_STREAM面向连接,面向字节流,可靠传输。TCP类型
SOCK_DGRAM,无连接,面向数据报,不可靠传输。UDP类型
protocol:协议类型 0是由系统自动选择的 IPRPROTO_TCP,IPRPROTO_UDP
返回值:成功返回套接字描述符,失败返回-1
bind ();
函数原型:int bind (int sockefd , const struct socketaddr * addr , socetlen_t addrlen);
参数介绍:
socketfd:socket();函数返回的描述符将socket绑定到指定的IP和端口上面,当socket返回描述符时,只是存在于其协议族中并没有实际分配协议地址。
addr:要绑定的地址信息 ipv4 是struct sockaddr_in ipv6 是struct sockaddr_in6
addrler:地址信息长度,通常设置为sizeof(struct sockaddr)。
struct socketaddr_in
{
sin_family;
sin_port;
sin_addr.s_addr;
}
返回值 0是成功 -1代表出错
sendto();
函数原型:sszie_t(int socketfd ,const void *buf, size_t. len ,int flags,const struct sockaddr*dest_addr , socklen_t addrlen );
参数介绍:
socketfd:发送套接字的描述符 因为是用于非可靠链接UDP的数据发送,不用先建立连接,只需要给出目的协议地址就好了。
buf:要发送的数据空间首地址,因为UDP没有真正的发送缓冲区,因为是不可靠链接,不必保存应用里面的数据拷贝,因为应用进程中的数据沿协议向下传递的过程中,会以某种形式拷贝到内核缓冲区,当数据链路层把数据发出去的时候,内核缓冲区就会把数据删除,所以他不需要一个发送缓冲区
len:要发送的数据长度
flag:选项标志 0 默认为阻塞发送
dest_addr:对端地址信息
addrlen:地址信息长度
返回值:返回成功是实际发送的数据字节长度;失败返回-1;
recvfrom();
函数原型:ssize_t(int sockfd , void *buf , size_t len, int flags, struct sockaddr * src_addr , socklen_t * addrlen);
参数介绍:
sockfd:套接字描述符
buf:一块空间的首地址,用于存放从内核获取到的数据
len:要获取的数据长度
flag:默认0-阻塞接受
src_addr:当前接受的数据的发送方的地址信息
*addrlen:这是一个输入输出型参数,指定想要多长地址,返回实际长度
返回值:成功返回实际接收到的数据长度,失败返回-1;
函数实现
服务器
udp_srv.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>//struct sockaddr_in 结构
#include<stdlib.h>
#include<arpa/inet.h>//字节序转换接口文件
#include<sys/socket.h>//socket接口文件
int main()
{
//实现udp 服务器
//创建套接字
int sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sockfd < 0)
{
perror("socket error !");
return -1;
}
//绑定地址信息bind
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9090);
addr.sin_addr.s_addr = inet_addr("10.0.8.12");
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(sockfd,(struct sockaddr* ) &addr ,len );
if(ret < 0){
close(sockfd);
perror("bind error !");
return -1;
}
while (1){
//接受数据
char buf[1024] = {0};
struct sockaddr_in cliaddr;
ret = recvfrom(sockfd,buf,1023,0,(struct sockaddr *) &cliaddr,&len);
if(ret < 0){
perror("recvfrom error !");
close(sockfd);
return -1;
}
printf("客户端: %s : %d 说: %s \n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buf);
//发送数据
printf("服务器: ");
fflush(stdout);
char data [1024] = {0};
fgets(data,1023,stdin);
ret = sendto(sockfd,data,strlen(data),0,(struct sockaddr*)&cliaddr,len);
if(ret < 0){
perror("sento error !");
close (sockfd);
return -1;
}
}
// 关闭套接字
close(sockfd);
return 0;
}
udp_scoket.h
//封装一个udpsocket类
//通过实例化对象来调用成员函数完成客户端服务器搭建
#include<iostream>
#include<string>
#include<netinet/in.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
using namespace std;
class UdpSocket{
private:
int _sockfd;
public:
UdpSocket():_sockfd(-1){}
~UdpSocket()
{
if(_sockfd != -1)
{
close(_sockfd);
}
}
bool Socket(){
_sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(_sockfd < 0)
{
perror("socket error !");
return false;
}
return true;
}
bool Bind(const 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 len = sizeof(struct sockaddr_in);
int ret;
ret = bind(_sockfd,(struct sockaddr*)&addr,len);
if(ret < 0)
{
perror("bind error !");
return false;
}
return true;
}
bool Send(const string &data,const 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 len = sizeof(struct sockaddr_in);
int ret;
ret = sendto(_sockfd,data.c_str(),data.size(),0,(struct sockaddr*) &addr,len);
if(ret < 0)
{
perror("sendto error");
return false;
}
return true;
}
bool Recv(string *buf, string *ip = NULL,uint16_t *port = NULL)
{
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
char temp [4096] = {0};
int ret;
ret = recvfrom(_sockfd,temp,4096,0,(struct sockaddr *)&addr,&len);
if(ret < 0){
perror ("recvfrom error !");
return false;
}
if(ip != NULL)
{
*ip = inet_ntoa(addr.sin_addr);
}
if(port != NULL){
*port = ntohs(addr.sin_port);
}
*buf = temp;
return true;
}
bool Close()
{
if(_sockfd != -1)
{
close(_sockfd);
}
return true;
}
};
udp_cli.cpp
#include"udp_socket.hpp"
using namespace std;
#define CHECK_RES(q) if((q)==false) {return -1;}
int main(int argc,char *argv[])
{
if(argc != 3){
cout<< "Usage : ./udp_cli 10.0.8.12 9090" << endl;
cout << "Server Address !" << endl;
return -1;
}
string srv_ip = argv[1];
int srv_port = stoi(argv[2]);
UdpSocket sock;
CHECK_RES(sock.Socket());
while(1)
{
cout << "客户端 : " ;
fflush(stdout);
string buf;
cin >> buf;
CHECK_RES(sock.Send(buf,srv_ip,srv_port));
buf.clear();
CHECK_RES(sock.Recv(&buf));
cout << "服务器 : " << buf << endl;
}
CHECK_RES(sock.Close());
return 0;
}