今天翻到一篇不错的技术分享,看完之后自己也琢磨了一下,把思路梳理记录下来。
文章目录
动手通信(Start)服务端源码-V1 UdpClient 测试代码注意事项完整源码 二、V2 版本 - DictionaryServer 三、V3 版本 - ChatSystemDemo四、windows和linux之间进行通信一、V1 版本 - echo server
UdpServer
服务端先从网络中读消息之后再写。
初始化套接字(Init)
系统调用socket
创建套接字的系统调用:
参数一:
协议族/域,用来告诉套接字是做本地通信还是网络通信还是其他。
参数二:
套接字类型:
返回值:
现在我们调用socket只是把网络在文件系统中打开了,还需要给文件绑定ip地址和端口号,这就要用到下面的bind。
bind
套接字编程中,bind 是将【IP 地址 + 端口号】绑定到 套接字(socket)文件描述符 上,而不是直接绑定到进程。
在介绍bind之前小编先介绍几个接下来几个常用的结构体:
struct sockaddr 通用地址结构 函数参数通用类型
struct sockaddr_in IPv4 地址结构 实际存放 IP + 端口
struct sockaddr_in6 IPv6 地址结构 IPv6 使用
第一个参数传递socket的返回值。
第二个参数传递一个填充好ip地址和端口号的结构体:
第三个参数传递结构体的大小。
返回值等于0表示绑定成功,不等于0表示失败。
在介绍bind代码代码达成之前小编先介绍两个需要包的头文件:<netinet/in.h>头文件能提供各种数据类型(sockaddr_in就在该头文件中),<arpa/inet.h>能提供各种大小端转化的方法,加上之前介绍的<sys/types.h>和<sys/socket.h>,这四个头文件是我们网络编程时几乎务必包含的四个头文件。
bind代码分两步达成:
1、填充ip和port
在填充ip和port之前,还需要在 struct sockaddr_in 结构体中,将sin_family 字段赋值为 AF_INET,用来告诉操作系统:这个套接字使用 IPv4 地址格式来进行网络通信,后续的 IP 地址和端口号都要按照 IPv4 协议来解析和处理。
首先要创建一个sockaddr_in类型结构体,之后把它的内存空间清零,我们可以用memset,小编介绍一种新方法,用bzero,需要包<strings.h>头文件,它可以将一段指定的内存空间清零。
ip和端口号需要通过命令行参数传递,所以udpserver.cc中的main函数要接受命令行参数,之后创建UdpServer对象时再把接收到的ip和端口号以参数的形式传递。
填入端口号_port时要把小端转大段端htons(_port),主机字节序转网络字节序),因为端口号也要随着报文一起发给对方主机,这样对方主机在回消息时才知道发送方在哪里。(htons中的s表示short,将16 位无符号短整型 从主机字节序转换为网络字节序)
填入ip地址要把可读性好的字符串风格的点分十进制ip转化为4个字节的整数ip,方便网络传输,这里需要用到inet_addr,它不仅可以将字符串风格的点分十进制ip转为整数ip,并且它的返回值已经是网络字节序,所以不需要再通过htons转换。
还需要注意填ip地址时要这样:local.sin_addr.s_addr,因为sin_addr是结构体,C++规定结构体不能整体赋值,所以只能对sin_addr内部的整型变量s_addr赋值。
2、sockaddr_in和socketfd进行bind
调用bind系统调用即可。
动手通信(Start)
目前服务器已经初始化完成,接下来我们就要基于文件描述符来进行通信了。
1、读取数据
因为udp不是面向字节流的,所以不能直接用read,udp有一套自己的读取、发送数据接口:
recvfrom:
参数:
1、接收方文件描述符。
2、输出型参数,用户自定义一段缓冲区,将读到的数据带出。
3、表示参数2用户自定义缓冲区的大小。
4、表示读取策略,默认置为0,表示阻塞读取。
5、输出型参数,是对端主机的ip和端口号信息,是一个sockaddr_in类型的结构体变量,方便我们回消息。(套接字编程:发送数据 = 消息内容 + 发送主体)
6、输入输出型参数,表示参数5的结构体大小,输入表示传入的结构体大小,输出表示实际结构体大小。
返回值:
大于0表示实际读取多少字节,小于0表示读取失败。
2、发送数据
sendto:
服务端源码-V1
```
//UdpServer.hpp
pragma once
include
include
include
include
include
include
include
include
include "Logger.hpp"
static const int gdefaultsockfd = -1;
class UdpServer
{
public:
UdpServer(const std::string &ip, const int port)
:_ip(ip)
,_port(port)
,_sockfd(gdefaultsockfd)
{
}
void Init()
{
// 1、创建套接字fd
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(_sockfd < 0)
{
//创建套接字失败
LOG(LogLevel::FATAL)
暂时整理到这里。以上都是个人理解,可能有疏漏,欢迎指正。
评论 (0)
暂无评论