对于UDP通信:是一种数据报格式的通信是不需要建立链接的,只需要底层一直接受消息,然后发送给上层,上层一直阻塞式的接受消息即可。
对于TCP通信:TCP通信是一种建立链接的通信,是需要握手的过程,所以对于tcp而言,不能直接发送消息,需要不断的获取链接(accept)。并且TCP是面向字节流的,TCP网络也是面向字节流的,具有更通用的文件属性,并且是全双工的。
UDP通信服务端的相关代码
#pragma once
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
namespace udpserver
{
using namespace std;
enum
{
SOCK_ERROR = 1,
BIND_ERROR
};
static const uint16_t defaultport = 8080;
class udpServer
{
public:
udpServer(const uint16_t port = defaultport)
: _sockfd(-1), _port(defaultport)
{
}
void initServer()
{
// 1.创建套接字
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockfd < 0)
{
cout << "socket create error" << endl;
exit(SOCK_ERROR);
}
cout << "socket create success" << endl;
// 绑定---将用户栈的数据让网络知道
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port); // 这里需要主机转网络 inet_addr 2个作用 把string---->uint32_t 大端转换
local.sin_addr.s_addr = INADDR_ANY; // 绑定任意端口即可
int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
if (_sockfd < 0)
{
cout << "bind create error" << endl;
exit(BIND_ERROR);
}
cout << "bind create success" << endl;
// 到这里udp的初始化算是完成
}
void startServer()
{
string message;
char buffer[1024];
for (;;)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
// udp首先需要接受消息
ssize_t recvnum = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
if (num < 0)
continue;
buffer[recvnum] = 0;
message = buffer;
message += "###udpserver send###";
// 开始返回数据
// a.首先获取目标地址和ip
string clientip = inet_ntoa(peer.sin_addr); // 将in_addr类型的数据转化成string类型的
uint16_t clientport = ntohs(peer.sin_port); // 网络转主机
handleMessage(clientip,clientport,message);
}
}
//一般是放到udpserver.hpp文件中进行解耦操作,hpp就不需要管处理函数了
void handleMessage(const string&clientip,const uint16_t* clientport,const string& message)
{
struct sockaddr_in sendAddr;
bzero(&sendAddr, sizeof(sendAddr));
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = htons(clientport);
sendAddr.sin_addr.s_addr = inet_addr(clientip);
sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&sendAddr, sizeof(sendAddr));
}
~udpServer() {}
private:
int _sockfd;
uint16_t _port;
};
}
主要是以下操作:
//1 .创建套接字
int socket(int domain, int type, int protocol); //返回文件描述符
//2.创建sockaddr_in 获取源ip和port 源ip一般设置为INADDR_ANY
struct sockaddr_in local;
memset(&local, 0, sizeof(local)); //bzero(&local,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);// 这里需要主机转网络inet_addr2个作用把string->uint32_t大端转换
local.sin_addr.s_addr = INADDR_ANY; // 绑定任意端口即可
//3.bind操作,将上面设置的用户栈操作设置到网路层中
int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
//上述upd的初始化就完成
//接受和发消息
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen); //src_addr addrlen是输入输出型参数
//发消息-->需要知道目的ip和目的端口,从recvfrom的src_addr中获取
struct sockaddr_in sendAddr;
bzero(&sendAddr, sizeof(sendAddr));
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = htons(clientport);
sendAddr.sin_addr.s_addr = inet_addr(clientip);
sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&sendAddr, sizeof(sendAddr));
TCP通信服务端的相关代码
#pragma once
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
namespace tcpServer
{
using namespace std;
enum
{
SOCK_ERROR = 1,
BIND_ERROR
};
static const uint16_t defaultport = 8080;
static const int backlog = 5;
class tcpserver
{
public:
tcpserver(const uint16_t &port)
: _listensockfd(-1), _port(port)
{
}
void initTcpserver()
{
// 1.tcp套接字
_listensockfd = socket(AF_INET, SOCK_STREAM, 0);
// 2. bind操作
struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY;
if (bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
{
cout << "bind error" << endl;
}
cout << "bind success" << endl;
// 3.需要监听谁要进行链接,返回的是一个文件描述符
int sockfd = listen(_listensockfd, backlog);
if (sockfd < 0)
{
cout << "listen error" << endl;
}
cout << "listen success :" << sockfd << endl;
}
void startTcpserver()
{
for (;;)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//建立链接之后,开始接受
int sock = accept(_listensockfd, (sockaddr *)&peer, &len);
if (sock < 0)
{
cout << "accept error" << endl;
}
cout << "accept success" << endl;
servceIO(sock);
close(sock);
}
}
void servceIO(int sock)
{
while (true)
{
#define NUM 1024
char buffer[NUM];
//从传输层的缓冲区复制数据
ssize_t num = read(sock, buffer, sizeof(buffer) - 1);
if (num > 0)
{
string message = buffer;
message += "server echo###";
//将应用层的数据复制到传输层中
write(sock, message.c_str(), message.size());
}
else
{
cout << "client quit,me too!" << endl;
break;
}
}
}
~tcpserver() {}
private:
int _listensockfd;
uint16_t _port;
};
}
TCP主要是完成一下操作
{
//1. socket操作
_listensock = socket(AF_INET,SOCK_STREAM,0); //sock
if(_listensock<0)
{
exit(0)
}
//2. bind
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port); // 主机转网络这儿需要进行一次转换
local.sin_addr.s_addr = INADDR_ANY; // 不需要绑定特定的端口号所有的底层网络收到的消息都往上传
if(bind(_listensock,(sockaddr*)&local,sizeof(local))<0)
{
exit(0);
}
//3. listen操作
#define backlog 5
if(listen(_listensock,backlog)<0)
{
exit(0);
}
}
{
for(; ;)
{
struct sockaddr_in peer;
socketlen_t len = sizeof(peer);
int fd =accept(_listensock,(struct sockaddr*)&peer,&len); //输入输出型参数
//fd 是文件描述符
if(fd<0)
{
exit(0)
}
servceIO(fd);
close(fd);
}
}
void servceIO()
{
while (true)
{
#define NUM 1024
char buffer[NUM];
//从传输层的缓冲区复制数据
ssize_t num = read(sock, buffer, sizeof(buffer) - 1);
if (num > 0)
{
string message = buffer;
message += "server echo###";
//将应用层的数据复制到传输层中
write(sock, message.c_str(), message.size());
}
else
{
cout << "client quit,me too!" << endl;
break;
}
}
}