概念图
创建服务器让A,B主机完成通信。
认识接口
socket
返回值:套接字,你可以认为类似fd
参数:
- domain->:哪种套接字,常用AF_INET(网络套接字)、AF_LOCAL(本地套接字)
- type->:发送数据类型,常用 SOCK_DGRAM(以数据报式发送)
- protocol->:一般填0,自动推导类型或者IPPROTO_UDP、IPPROTO_TCP。
创建一个套接字,类似创建一个文件标识符fd。
先介绍些结构体类型
struct sockaddr
struct sockaddr_in
struct sockaddr_un
_in结构体中保存的是ip\port数据,而_un中保存的则是本地的数据
udp协议为了本地通信与网络通信同一套接口兼容,所以先将sockaddr_in/_un强转成sockaddr类型传入各个函数,在函数中判断前2个字节类型,来做本地通信或者网络通信。
bind
将套接字绑定,一般来说套接字绑定都是服务器才会绑定的,客户端一般给操作系统自动分配ip与端口的。
返回值:成功0,失败-1.设置错误码
参数:
- sockfd 需要绑定的套接字
- sockaddr包含了需要与套接字绑定的ip和端口号。
- addrlen该结构体长度。
recvfrom
用来接收数据的接收
返回值:实际接收数据的长度,-1失败
参数
- sockfd:将从该套接字的端口和ip中取得数据
- buff:输出型参数,将数据存放到buff中。
- len:buff的长度
- flags:以状态等待数据,一般填0,阻塞等待数据
- src_addr:发送方ip+port结构体数据,输出型参数
- 结构体数据长度
sendto
发送数据给某个主机
返回值:实际发送数据的个数,-1失败
参数
- sockfd:将发送方的ip+port发送给对方
- buff:输入型参数,将buff中数据发送给对方。
- len:buff的长度;
- flags:默认发送方式发送
- 接收方ip+port结构体数据,根据该参数寻找对于主机
- 结构体数据长度
sockaddr_in结构体配套函数
机器大小端的转换函数。h本地to转
以太网规定,网络传输数据一定是大端方式传输,所以我们的机器无论是大端还是小端,在网络中都会成为大端序列。
当是为了人能看的明白,我们的ip地址一般都是以字符串的方式呈现,这样的方式我们称为点分十进制的方式。而且传入为了的ip地址可能需要改序列,所以一批接口出现了。
这些接口将字符串转为uint32_t或者将uint32_t转为字符串
代码
一份2个主机通过服务器可以聊天的代码
服务器代码:
server.hpp
#pragma once
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include <unordered_map>
using std::cin;
using std::cout;
using std::endl;
class udp_server
{
typedef int socket_t;
void ip_type()
{
cout << "ip:" << _ip << endl;
if (_ip == "127.0.0.1")
{
cout << "#####本地测试#####" << endl;
}
else if (_ip.empty())
{
cout << "#####开放全部ip地址#####" << endl;
}
else
{
cout << "#####指定ip地址#####" << endl;
}
}
void init_server()
{
_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_socket < 0)
{
Log(FATAL, "socket get fail!!![%d][%s]\n", __LINE__, __TIME__);
exit(1);
}
struct sockaddr_in ip_port;
bzero(&ip_port, sizeof(ip_port));
ip_port.sin_family = AF_INET;
ip_port.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
ip_port.sin_port = htons(_port);
if (bind(_socket, (struct sockaddr *)&ip_port, sizeof(ip_port)) < 0)
{
Log(FATAL, "bind fail!!![%d][%s]\n", __LINE__, __TIME__);
exit(2);
}
Log(NORMAL, "udp init success!!![%d][%s]\n", __LINE__, __TIME__);
}
public:
udp_server(uint16_t port, std::string ip = "") : _ip(ip), _port(port) {}
udp_server() {}
void activate()
{
init_server();
ip_type();
while (1)
{
char buff[1024] = {0};
// cout<<"buff size: "<<strlen(buff)<<endl;
struct sockaddr_in client_ip_port;
socklen_t len = sizeof(client_ip_port);
//开始等待客户端
ssize_t end = recvfrom(_socket, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&client_ip_port, &len);
char retbuff[1024]={0};
// cout<<"buff size: "<<strlen(buff)<<endl;
if (end >= 0)
{
buff[end] = '\0';
printf("[%s:%d]clint say# %s\n", inet_ntoa(client_ip_port.sin_addr), ntohs(client_ip_port.sin_port), buff);
sprintf(retbuff,"[%s:%d]clint say# %s", inet_ntoa(client_ip_port.sin_addr), ntohs(client_ip_port.sin_port), buff);
}
else
{
Log(WARINNG, "recvfrom Message have fail [%d][%s]\n", __LINE__, __TIME__);
}
//处理数据。
char key[64];
snprintf(key,sizeof(key),"%s-%u",inet_ntoa(client_ip_port.sin_addr),client_ip_port.sin_port);
auto it = _users.find(key);
if(it==_users.end())
{
cout<<key<<endl;
cout<<key<<" :放入客户端"<<endl;
_users.insert({key,client_ip_port});
}
// cout<<"end : "<<end<<endl;
//反馈
for(auto&it:_users)
{
if(client_ip_port.sin_addr.s_addr==it.second.sin_addr.s_addr)
{
continue;
}
sendto(_socket, retbuff, sizeof retbuff, 0, (struct sockaddr *)&(it.second), sizeof(it.second));
}
Log(Debug, "sendto Message have fail [%d][%s]\n", __LINE__, __TIME__);
}
}
private:
socket_t _socket;
std::string _ip;
uint16_t _port;
std::unordered_map<std::string, struct sockaddr_in> _users;
};
server.cpp
#include "server.hpp"
#include <memory>
using std::shared_ptr;
void SERVERUER()
{
std::cout<<"./server + ip + port"<<std::endl;
}
int main(int argc,char*argv[])
{
if(argc==2||argc==3)
{
cout<<"argc:"<<argc<<endl;
if(argc==2)
{
in_port_t port=atoi(argv[1]);
shared_ptr<udp_server> server_ptr(new udp_server(port));
server_ptr->activate();
}
else
{
in_port_t port=atoi(argv[2]);
std::string ip=argv[1];
shared_ptr<udp_server> server_ptr(new udp_server(port,ip));
server_ptr->activate();
}
}
else
{
SERVERUER();
}
return 0;
}
客户端代码
client.cpp
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include <pthread.h>
using std::cin;
using std::cout;
using std::endl;
typedef int socket_t;
void CLIENTUER()
{
std::cout << "./client + ip + port" << std::endl;
}
void *thread_func(void *ages)
{
socket_t *_socket = (socket_t*)ages;
char get_messages[102];
struct sockaddr_in temp;
bzero((void *)&temp, sizeof(temp));
socklen_t len(sizeof temp);
while (1)
{
ssize_t end = recvfrom(*_socket, get_messages, sizeof(get_messages) - 1, 0, (struct sockaddr *)&temp, &len);
if (end >= 0)
{
get_messages[end] = 0;
std::cout<< get_messages << std::endl;
}
}
return nullptr;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
CLIENTUER();
exit(1);
}
socket_t _socket = socket(AF_INET, SOCK_DGRAM, 0);
pthread_t tid;
pthread_create(&tid,nullptr,thread_func,(void*)&_socket);
std::string messages;
struct sockaddr_in server_ip_port;
bzero(&server_ip_port, sizeof(server_ip_port));
server_ip_port.sin_family = AF_INET;
server_ip_port.sin_addr.s_addr = inet_addr(argv[1]);
server_ip_port.sin_port = htons(atoi(argv[2]));
socklen_t len = sizeof(server_ip_port);
while (1)
{
fflush(stdout);
std::getline(std::cin, messages);
sendto(_socket, messages.c_str(), messages.size(), 0, (struct sockaddr *)&server_ip_port, len);
}
return 0;
}