聊城正规网站建设公司电话,公司的管理方式与管理方法,深圳小程序外包开发,做二手车有哪些网站有哪些手续费文章目录 十、网络基础5. socket编程socket 常见APIsockaddr结构简单的UDP网络程序 未完待续 十、网络基础
5. socket编程
socket 常见API
// 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器)
int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服… 文章目录 十、网络基础5. socket编程socket 常见APIsockaddr结构简单的UDP网络程序 未完待续 十、网络基础
5. socket编程
socket 常见API
// 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器)
int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同。 IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。 我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型端口号IP地址。
简单的UDP网络程序
我们需要我们之前写的日志程序 Log.hpp
#pragma once#include iostream
#include fstream
#include cstdio
#include string
#include ctime
#include cstdarg
#include sys/types.h
#include unistd.h
#include pthread.h
#include LockGuard.hpp// 宏定义用于定义日志格式
#define LOG(level, format, ...) do{LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__);}while (0)
// 将日志输入到文件
#define EnableFile() do{gIsSave true;}while (0)
// 将日志输出到显示器
#define EnableScreen() do{gIsSave false;}while (0)bool gIsSave false;
// 日志文件名
const std::string logname log.txt;// 枚举日志级别
enum Level
{DEBUG 0,INFO,WARNING,ERROR,FATAL
};// 保存日志到文件
void SaveFile(const std::string filename, const std::string message)
{std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out message;out.close();
}// 日志级别转字符串
std::string LevelToString(int level)
{switch (level){case DEBUG:return Debug;case INFO:return Info;case WARNING:return Warning;case ERROR:return Error;case FATAL:return Fatal;default:return Unknown;}
}// 获取当前时间字符串
std::string GetTimeString()
{time_t curr_time time(nullptr);struct tm *format_time localtime(curr_time);if (format_time nullptr)return None;char time_buffer[1024];snprintf(time_buffer, sizeof(time_buffer), %d-%d-%d %d:%d:%d,format_time-tm_year 1900,format_time-tm_mon 1,format_time-tm_mday,format_time-tm_hour,format_time-tm_min,format_time-tm_sec);return time_buffer;
}// 日志锁同一时刻只能写一个日志
pthread_mutex_t lock PTHREAD_MUTEX_INITIALIZER;// 日志信息
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{// 日志级别std::string levelstr LevelToString(level);// 时间std::string timestr GetTimeString();// 进程idpid_t selfid getpid();// 日志内容char buffer[1024];va_list arg;va_start(arg, format);vsnprintf(buffer, sizeof(buffer), format, arg);va_end(arg);// 日志格式化std::string message [ timestr ] [ levelstr ] [ std::to_string(selfid) ] [ filename ] [ std::to_string(line) ] buffer;LockGuard lockguard(lock);// 输出日志if (!issave){std::cout message;}else{SaveFile(logname, message);}
}封装的RAII模式的锁 LockGuard.hpp
#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__#include iostream
#include pthread.hclass LockGuard
{
public:// 构造函数加锁LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}// 析构函数解锁~LockGuard(){pthread_mutex_unlock(_mutex);}
private:pthread_mutex_t *_mutex;
};#endif服务端代码 UdpServer.hpp
#pragma once#include iostream
#include string
#include cerrno
#include cstring
#include cstdlib
#include strings.h
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h
#include netinet/in.h
#include Log.hpp
#include InetAddr.hppenum
{SOCKET_ERROR 1,BIND_ERROR,USAGE_ERROR
};const static int defaultfd -1;class UdpServer
{
public:UdpServer(uint16_t port):_sockfd(defaultfd),_port(port),_isrunning(false){}void InitServer(){// 创建 UDP socket 套接字_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){LOG(FATAL, socket error, %s, %d\n, strerror(errno), errno);exit(SOCKET_ERROR);}LOG(INFO, socket create success, sockfd: %d\n, _sockfd);struct sockaddr_in local;bzero(local, sizeof(local));local.sin_family AF_INET;// 端口号要经过网络传输需要转换成网络字节序local.sin_port htons(_port);local.sin_addr.s_addr 0;// 绑定本地地址int n bind(_sockfd, (struct sockaddr*)local, sizeof(local));if (n 0){LOG(FATAL, bind error, %s, %d\n, strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, socket bind success\n);}void Start(){// 启动服务器_isrunning true;while (true){// 接收数据char buffer[1024];struct sockaddr_in peer;socklen_t len sizeof(peer);ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)peer, len);if (n 0){// 收到数据打印buffer[n] \0;InetAddr addr(peer);LOG(DEBUG, get message from [%s:%d]: %s\n, addr.Ip().c_str(), addr.Port(), buffer);sendto(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)peer, len);}// sleep(1);// LOG(DEBUG, server is running...\n);}_isrunning false;}~UdpServer(){}
private:int _sockfd;// std::string _ip;// 服务器所用端口号uint16_t _port;bool _isrunning;
};客户端代码 UdpClient.hpp
#include iostream
#include string
#include cstring
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include cstdlibvoid Usage(std::string proc)
{std::cout Usage:\n\t proc local_ip local_prot\n std::endl;
}int main(int argc, char* argv[])
{if (argc ! 3){Usage(argv[0]);exit(1);}// 获取本地IP地址和端口号std::string serverip argv[1];uint16_t serverport std::stoi(argv[2]);int sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0){std::cerr socket error std::endl;}// 构建目标主机的socket信息struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_port htons(serverport);server.sin_addr.s_addr inet_addr(serverip.c_str());std::string message;// 循环发送消息while (true){// 输入消息std::cout Please Enter# ;std::getline(std::cin, message);sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)server, sizeof(server));// 接收消息struct sockaddr_in peer;socklen_t len sizeof(peer);char buffer[1024];ssize_t n recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)peer, len);if (n 0){std::cout server echo# buffer std::endl;}}return 0;
}发送者数据 InetAddr.hpp
#pragma once#include iostream
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h
#include netinet/in.hclass InetAddr
{
private:void GetAddress(std::string *ip, uint16_t *port){*port ntohs(_addr.sin_port);*ip inet_ntoa(_addr.sin_addr);}public:InetAddr(const struct sockaddr_in addr) : _addr(addr){GetAddress(_ip, _port);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}
private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};Main.cc
#include iostream
#include memory
#include UdpServer.hppvoid Usage(std::string proc)
{std::cout Usage:\n\t proc local_prot\n std::endl;
}int main(int argc, char* argv[])
{if (argc ! 2){Usage(argv[0]);exit(USAGE_ERROR);}EnableScreen();// std::string local_ip argv[1];uint16_t port std::stoi(argv[1]);std::unique_ptrUdpServer usvr(new UdpServer(port));// 初始化服务器usvr-InitServer();// 启动服务器usvr-Start();return 0;
}Makefile
.PHONY:all
all:udpserver udpclientudpserver:Main.ccg -o $ $^ -stdc11
udpclient:UdpClient.ccg -o $ $^ -stdc11
.PHONY:clean
clean:rm -f udpserver udpclient结果 服务端输入./udpserver 端口号 即可 客户端输入./udpclient 服务器的ip地址若是本地互联输入0即可 端口号 即可 成功通信 未完待续