1.IP地址
IP地址是在IP协议中, 用来标识网络中不同主机的地址。由点分十进制组成,在数据传输中IP地址是一直不变的。
在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址和目的IP地址。
2.端口号
由2字节16位整数组成,标识当前主机的唯一进程。与进程的pid有类似作用,但端口号是专用进行网络通信。
注意:一个端口号和一个进程相关联,一个进程可以和多个端口号相关联。
传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号,就是在描述 "数据是谁发的, 要 发给谁"。
3.TCP和UDP协议
这里简单说一下,TCP(Transmission Control Protocol 传输控制协议)和UDP(User Datagram Protocol 用户数据报协议)协议都是传输层协议。TCP是有连接的,面向字节流的可靠传输;UDP是无连接,面向数据报的不可靠传输。
4.网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分。但在网络中发送数据不论发送方的主机是大端机还是小端机,都要将数据转换为大端才能发送数据到网络数据流,而接收方也必须按照大端的接受方式收取数据,不论接收方是大端机还是小端机。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
#include<arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htonl(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohl(uint16_t hostshort);
//h表示host,n表示network,l表示32位长整数,s表示16位短整数
//例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送
5.socket及其接口
Socket,全称“套接字”,是一种编程接口,主要用于网络应用程序之间进行通信。它是由操作系统提供的API(Application Programming Interface),使得程序能够创建、连接、监听网络端口并进行数据交换。在计算机网络中,每一个通信都是通过两个独立的套接字进行的,一个是客户端的套接字,另一个是服务器的套接字。在TCP/TCP/IP协议的四层模型中,它是第四层即传输层的应用接口。
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);
domain:指定通信域,常用有:AF_UNIX/AF_LOCAL(unix7),AF_INET(ipv4),AF_INET6(ipv6/7)。
type:指定套接字类型即使用TCP/UDP协议,SCOCK_STREAM(TCP)/SOCK_DGRAM(UDP)。
sockaddr:规定不同的软件/系统使用网络接口统一数据传输的类型。sockaddr有两个子类:sockadd_in(AF_INET),sockadd_un(AF_UNIX)。
下面是一个tcp服务器实例:
#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;
class tcpserve
{
public:
tcpserve(uint16_t _port) :port(_port),isrunning(false)
{}
void Init()
{
listensock=socket(AF_INET,SOCK_STREAM,0);
if(listensock<0)
{
cout<<"socket errr,"<<errno<<":"<<strerror(errno)<<endl;
exit(-1);
}
struct sockaddr_in local;
bzero(&local,sizeof(local));
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=htonl(INADDR_ANY);//特殊的ip可以接受所有网络接口的请求
int n=bind(listensock,(struct sockaddr*)&local,sizeof(local));
if(n!=0)
{
cout<<"bind errr,"<<errno<<":"<<strerror(errno)<<endl;
exit(-2);
}
cout<<"bind success,listensock"<<listensock<<endl;
//tcp特有的,设置监听状态
n=listen(listensock,1);
if(n!=0)
{
cout<<"listen errr,"<<errno<<":"<<strerror(errno)<<endl;
exit(-3);
}
cout<<"listen success,listensock"<<listensock<<endl;
}
void Serve(int sockfd)
{
char buffer[1024];
while(true)
{
ssize_t n=read(sockfd,buffer,sizeof(buffer)-1);
if(n>0)
{
buffer[n]=0;
cout<<"client say#"<<buffer<<endl;
string message="serve echo#";
message+=buffer;
write(sockfd,message.c_str(),message.size());
}
else if(n==0)
{
cout<<"client quit..."<<endl;
break;
}
else if(n<0)
{
cout<<"read errno,errno code:"<<errno<<"errno string:"<<strerror(errno)<<endl;
break;
}
}
}
void Start()
{
isrunning=true;
while(isrunning)
{
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
int n=accept(listensock,(struct sockaddr*)(&peer),&len);
if(n<0)
{
cout<<"accept errno,errno code:"<<errno<<"errno string:"<<strerror(errno)<<endl;
continue;
}
cout<<"accept success,get a new sockfd:"<<n<<endl;
Serve(n);
}
}
~tcpserve()
{}
private:
uint16_t port;
int listensock;
bool isrunning;
};
下面是一个tcp客户端:
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char *argv[])
{
if(argc!=3)
{
cout<<"请输入正确的命令"<<endl;
return 1;
}
string ip=argv[1];
uint16_t port=stoi(argv[2]);
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
cout<<"socket errr,"<<errno<<":"<<strerror(errno)<<endl;
return -1;
}
cout<<"socket success,sockfd"<<sockfd<<":"<<ip<<endl;
struct sockaddr_in local;
bzero(&local,sizeof(local));
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=inet_addr(ip.c_str());
//建立连接
int n=connect(sockfd,(struct sockaddr*)&local,sizeof(local));
if(n<0)
{
cout<<"connect errr,"<<errno<<":"<<strerror(errno)<<endl;
return -2;
}
while(true)
{
string message;
cout<<"Please Enter#";
getline(cin,message);
n=write(sockfd,message.c_str(),message.size());
if(n>0)
{
char buffer[1024];
ssize_t m=read(sockfd,buffer,sizeof(buffer)-1);
if(m>0)
{
buffer[m]=0;
cout<<"get a echo message:"<<buffer<<endl;
}
else break;
}
else break;
}
}