一.协议分层
二.局域网
三.网络编程套接字
1.ip地址,端口号(port)
ip地址用来标识互联网中唯一的一台主机;
端口号用来标识该指定机器的唯一进程。
[ip, 端口号](套接字,socket):互联网中唯一的两个进程
2.一个端口号可以和多个进程关联? x
一个进程可以和多个端口号关联? √
3.TCP协议,UDP协议
TCP:可靠通信,如果中途数据出问题,会做出调整(做更多工作)
UDP:不管有没有出问题,正常发送(速度快)
4.网络字节序
机器发出的数据----a--->网络-----b--->目标机器接收
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。
5.网络编程,socket是有很多类别
unix socket:域间socket,同一台机器上的文件路径,命名管道,本主机内部通信。
网络socket:ip + port(端口号),网络通信。
原始socket:编写一些网络工具
6.sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、 IPv6
然而, 各种网络协议的地址格式并不相同。
四.网络编程
UdpServer.hpp
#pragma once
#include<iostream>
#include<string>
#include<strings.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"nocopy.hpp"
#include"Comm.hpp"
const static int defaultsize = 1024;
class UdpServer
{
public:
UdpServer(const std::string& ip, const uint16_t port)
:_ip(ip)
,_port(port)
{
}
void Init()
{
//1.创建socket,创建文件细节
sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 参数1.协议家族(AF_UNIX, AF_INET) 参数2.套接字类型 返回值:文件描述符
if(sockfd < 0)//创建失败
{
std::cout << "sockfd error" << std::endl;
exit(Socket_Err);
}
std::cout << "sock success" << std::endl;
//2.bind绑定指定网络信息
struct sockaddr_in local; //网络socket结构体 //内含服务端server的一些信息
bzero(&local, sizeof(local)); //同memset(&local, 0, sizeof(local))
local.sin_family = AF_INET; //协议家族,进行网络通信
local.sin_port = htons(_port); //端口号,htons将主机序列转化位网络序列
local.sin_addr.s_addr = INADDR_ANY; //存的是ip,我们不希望服务端绑定固定ip,采取INADDR_ANY任意绑定ip
int n = bind(sockfd, (struct sockaddr*)&local, sizeof(local)); //将进程绑定端口号
if(n != 0)
{
std::cout << "bind fail" << std::endl;
exit(Bind_Err);
}
}
void Start()
{
char buffer[defaultsize];
for(;;) //服务器永不退出
{
struct sockaddr_in peer; //客户端
socklen_t len = sizeof(peer); //不能乱写
ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len); //收信息,给我们客户端的socket信息
if(n > 0) //成功
{
buffer[n] = 0;
std::cout << "client say:" << buffer << std::endl;
sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, len); //发信息
}
}
}
~UdpServer()
{}
private:
std::string _ip;
uint16_t _port;
int sockfd;
};
Main.cc
#include"UdpServer.hpp"
#include<memory>
void Usage(std::string proc)
{
std::cout << "Usage: \n\t" << "local_ip local_port\n" << std::endl;
}
// ./udq_server 127.0.0.1 8888
//127.0.0.1:本地环回,通常用来进行网络OS的测试
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return Usage_Err;
}
std::string ip = argv[1];
uint16_t port = std::stoi(argv[2]);
std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(ip, port);
usvr->Init();
usvr->Start();
return 0;
}
Comm.hpp
#pragma once
enum
{
Usage_Err = 1,
Socket_Err,
Bind_Err
};
nocopy.hpp
#pragma once
#include<iostream>
class nocopy
{
public:
nocopy(){}
nocopy(const nocopy&) = delete;
const nocopy& operator=(const nocopy&) = delete;
~nocopy(){}
};
udpclient.cc
#include<iostream>
#include<string>
#include<cstring>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <unistd.h>
void Usage(const std::string& process)
{
std::cout << "Usage:" << process << "server ip" << " " << "server port" << std::endl;
}
// ./udp_client serverip serverport这样运行
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 1;
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
//1.创建socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0)
{
std::cerr << "socket error" << strerror(errno) << std::endl;
return 1;
}
//2.client需要bind,但我们不需要显示bind,让本地OS自动随机bind,选择随机端口号
//2.1填充server信息
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());
while(true)
{
std::string inbuffer;
std::cout << "Please say#";
getline(std::cin, inbuffer);
ssize_t n = sendto(sock, inbuffer.c_str(), sizeof(inbuffer), 0, (struct sockaddr*)&server, sizeof(server)); //发给server
//send时,随机bind端口号
if(n > 0)
{
char buffer[1024];
struct sockaddr_in temp;
socklen_t len = sizeof(temp);
ssize_t m = recvfrom(sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&temp, &len); //收server信息
if(m > 0)
{
buffer[m] = 0;
std::cout << "server echo#" << buffer << std::endl;
}
else break;
}
else
{
break;
}
}
close(sock);
return 0;
}
makefile
.PHONY:all
all:udp_server udp_client
udp_server:Main.cc
g++ -o $@ $^ -std=c++14
udp_client:udpclient.cc
g++ -o $@ $^ -std=c++14
.PHONY:clean
clean:
rm -rf udp_server udp_client