一. IP地址
1.IP地址是一个逻辑地址,32位,4个字节组成,常用点分十进制表示
2.网络中每台主机都有个IP地址,用来标识一台主机,具有全球唯一性
3.网络中的主机之间要进行交流,需要的是IP地址来确认
二.网络协议
是计算机之间数据交换的一种规则和标准,如果没有协议,数据是无法进行交换,正如两个人之间的交流,要使用同一种语言才能进行交流,计算机也是一样
1.OSI七层参考模型
在通信实体之间的对等层之间是不能通信的,并且一个通信实体上各层之间有严格的单向依赖,上层使用下层提供的服务,下层为上层提供服务,
而实体之间的实际通信是由最底层的物理层使用物理介质传输数据
(1).应用层:负责程序和网络服务的接口工作,使用报文的数据单元,处理的地址是进程的标识和端口号,使用到的协议有远程登录协议Telnet、文件传输协议FTP、
超文本传输协议HTTP、域名服务DNS、简单邮件传输协议SMTP、邮局协议POP3.
(2).表示层:定义数据的格式和加密数据,
(3).会话层:主要组织.协商和管理通信的应用程序进程之间的会话,对进程之间的消息进行管理,为会话的实体建立连接和维持联系状态
(4).传输层:在连接的实体之间建立传输链路,有两种协议可选择,
传输控制协议TCP: 面向连接的可靠的传输协议。
用户数据报协议UDP: 是无连接的,不可靠的传输协议。
处理的地址是进程标识.TCP端口和UDP端
(5).网络层:使用IP地址寻址,通过路由选择算法为数据分组通过通信子网选择最适当的路径,并提供网络的互联和拥塞功能,数据单元是分组数据,处理地址是IP地址,
使用的协议为网际协议IP、Internet互联网控制报文协议ICMP、Internet组管理协议IGMP.
(6).数据链路层:在相邻的节点间的链路上,进行以”帧”为单位的无差错的传输数据
(7).物理层:物理设备之间的通信
2.数据的封装
要从一台主机上发送数据到另外一台数据上,首先要进行打包封装,也就是在数据前面加上特定的协议头部,
3.TCP/IP模型
(1).有4层:应用层,传输层,网络层,网络接口
(2).TCP/IP模型与OSI七层模型
4.端口
传输层提供了进程的通信能力,为了标识实体中通信的进程,TCP/IP采用了协议端口,简称端口,程序与某个端口绑定后,传输层的数据的接收和发送都经过这个端口,
端口使用一个16位的数字来表示,它的范围是0~65535,1024以下的端口号保留给预定义的服务
三.客户机/服务器模式(C/S)编程
1. Socket(套接字)是一种网络编程接口,用于描述IP地址和端口,有了套接字后,可以很方便的访问TCP/IP协议,使得开发网络程序更加的容易,
流式套接字(SOCK_STREAM):提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。
数据报式套接字(SOCK_DGRAM): 提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
原始套接字(SOCK_RAW)。
2.服务器
首先服务器方要先启动
①打开一个通信通道并告知本地主机,它愿意在某一地址和端口上接收客户请求。
②等待客户请求到达该端口。
③接收到重复服务请求,处理该请求并发送应答信号。接收到并发服务请求,要激活一个新的进程(或线程)来处理这个客户请求。新进程(或线程) 处理此客户请求,
并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。
④返回第二步,等待另一客户请求。
⑤关闭服务器。
3.客户机
①打开一个通信通道,并连接到服务器所在主机的特定端口。
②向服务器发服务请求报文,等待并接收应答;继续提出请求。
③请求结束后关闭通信通道并终止。
4. 基于TCP(面向连接)的socket编程
5. 基于UDP(面向无连接)的socket编程
6.TCP编程实现
服务端:
1、初始化winsocket 库 //任何网络应用程序必须做的一步 :WSAStartup();
2、创建套接 socket();
3、绑定本机的某个ip地址和端口上 bind()
4、监听 listen()
5、接受用户的请求 accept(); //阻塞的函数,如果没有客户请求,将等待
6、通讯 send(),recv()
客户端:
1、初始化winsocket 库 //任何网络应用程序必须做的一步 WSAStartup();
2、创建套接字 socket();
3、连接服务端 connect();
TCP服务端代码......
//基于TCP网络编程,服务端
//VC6.0环境
//1. 加载套接字库
//使用函数int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
//第一参数是window套接字版本,可以使用MAKEWORD(x,y)获取版本
//第二参数是WSADATA结构体指针,用于接收加载的套接字信息
//2. 创建套接字,使用函数SOCKET socket(int af,int type,int protocol );
//第一参数是地址簇,对于TCP/IP协议,只能是AF_INET或PF_INET
//第二参数是指定Socket类型,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字
//第三参数是与特定的地址家族相关的协议,如果是0,系统自动选择合适协议
//3. 创建套接字后要绑定到一个IP地址和一个端口上
//函数:int bind(SOCKET s,const struct sockaddr FAR *name,int namelen);
//第一参数是要绑定的套接字,
//第二参数是套接字的本地地址信息,指向sockaddr结构的指针,可以使用sockaddr_in结构的指针
//指针代替,是按照网络字节顺序表示
//第三参数是指该地址结构长度
/*sockaddr_in结构体
struct sockaddr_in{
short sin_family; //地址簇,对于IP地址,一直是AF_INET
unsigned short sin_port; //给套接字的端口号
struct?? in_addr sin_addr; //in_addr结构体,套接字的IP地址
char sin_zero[8]; //使得sockaddr和sockaddr_in结构长度一样
};
***********************************************************/
/*in_addr结构体
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr; //使用的是这个
} S_un;
};
值得说明的两点:
1.将IP地址指定为INADDR_ANY,表示套接字可以向分配给本地多个的IP地址发送数据和接收数据,
如果只想使用一个IP地址,可以使用inet_addr()函数将一个点分十进制的IP地址转为u_long型,
而使用inet_ntoa()函数会完成相反的转换
2.sockaddr(sockaddr_in)是按照网络字节顺序表示,所以要进行相应的字节顺序转换
htonl()函数是将一个u_long型转换为TCP/IP网络字节顺序
htons()函数是将一个u_short型转换为TCP/IP网络字节顺序
*/
//4.监听int listen(SOCKET s, int backlog );
//第一参数是套接字
//第二参数是等待连接队列连接请求的个数,使用SOMAXCONN表示系统选择合适的个数
//5.接受连接请求
//SOCKET accept(SOCKET s,struct sockaddr FAR *addr, int FAR *addrle);
//第一参数是套接字
//第二参数是sockaddr结构体,用于保存连接的客户的地址信息
//第三参数是用于保存结构体大小,要初始化
//连接成功后返回一个套接字,可以使用这个套接字与连接的这个客户端通信
//6.发送消息
//int send(SOCKET s,const char FAR *buf,int len,int flags);
//第一参数是已经于客户端连接了的套接字
//第二参数是发送的数据
//第三参数是数据长度
//第四参数是调用执行方式,一般为0
//7.接受数据
//int recv(SOCKET s,char FAR *buf, int len,int flags);
//用法和send()函数差不多
#include <WinSock2.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"Ws2_32.lib") //连接库文件
int main()
{
WSADATA WSAD;//WSADATA结构体
SOCKET SvrSocket;//套接字
//加载套接字库
if(WSAStartup(MAKEWORD(2,2),&WSAD))
{
cout << "加载套接字库失败" << endl;
return 0;
}
//创建套接字,如果失败的话返回INVALID_SOCKET值
SvrSocket = socket(AF_INET,SOCK_STREAM,0);
if (INVALID_SOCKET == SvrSocket )
{
cout << "创建套接字失败" << endl;
WSACleanup();
return 0;
}
//绑定套接字
SOCKADDR_IN sockAddr;
sockAddr.sin_port = htons(6000);
sockAddr.sin_addr.S_un.S_addr =ADDR_ANY;
sockAddr.sin_family = AF_INET;
bind(SvrSocket,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));
//监听
listen(SvrSocket,5);//只可以连接最前连接的5个连接请求
cout << "服务器开始监听...." << endl;
//等待客户并连接
SOCKADDR_IN ClientAddr;
int ClientAddrLeng = sizeof(SOCKADDR);//赋初始值
while (TRUE)
{
SOCKET Socket1 = accept(SvrSocket,(SOCKADDR*)&ClientAddr,&ClientAddrLeng);
if (Socket1 != INVALID_SOCKET)
{
cout << inet_ntoa(ClientAddr.sin_addr) << "连接成功" << endl;
}
//发送数据
char buff[100];
sprintf(buff,"欢迎 %s 的到来",inet_ntoa(ClientAddr.sin_addr));
send(Socket1,buff,sizeof(buff),0);
//send(Socket1,buff,strlen(buff)+1,0);
//接受数据
char ReBuff[100];
recv(Socket1,ReBuff,sizeof(ReBuff),0);
cout <<inet_ntoa(ClientAddr.sin_addr)
<< "说:" <<ReBuff << endl;
closesocket(Socket1);
}
closesocket(SvrSocket);
WSACleanup();
return 0;
}
TCP客户端代码....
//基于TCP网络编程,客户端
//VC6.0环境
//1、初始化winsocket 库,任何网络应用程序必须做的一步
//WSAStartup();
//2、创建套接字
//socket();
//3、连接服务端
//connect();
//int connect(SOCKET s,const struct sockaddr FAR *name,int namelen );
//第一参数是客户端的套接字
//第二参数是服务端得地址信息
//第三参数地址结构体长度
#include <WinSock2.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"Ws2_32.lib") //连接库文件
int main()
{
WSADATA WSAD;//WSADATA结构体
SOCKET ClientSock;//套接字
//加载套接字库
if(WSAStartup(MAKEWORD(2,2),&WSAD))
{
cout << "加载套接字库失败" << endl;
return 0;
}
//创建套接字
ClientSock = socket(AF_INET,SOCK_STREAM,0);
if (INVALID_SOCKET == ClientSock)
{
cout << "创建套接字失败" << endl;
WSACleanup();
return 0;
}
//连接服务器
int err;
SOCKADDR_IN ServerAddr; //服务端地址结构体
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//服务端得IP地址,因为服务端在本机上
ServerAddr.sin_port = htons(6000); //要和服务端绑定的端口号一样
err= connect(ClientSock,(SOCKADDR*)&ServerAddr,sizeof(SOCKADDR));
//连接成功的话则返回0值
if (err)
{
cout << "连接服务器失败" << endl;
return 0;
}
else
{
cout << "连接服务器成功" << endl;
}
//接收消息和发送消息
char ReBuff[100];
recv(ClientSock,ReBuff,sizeof(ReBuff),0);
cout << "服务器消息: "<<ReBuff << endl;
send(ClientSock,"我是客户端...",sizeof("我是客户端...")+1,0);
closesocket(ClientSock);
WSACleanup();
return 0;
}
UDP服务端代码....
//基于UDP的网络编程,服务端
//VC6.0
/*UDP中的接收数据函数
int recvfrom(SOCKET s,char FAR* buf,int len,int flags,struct sockaddr FAR *from,int FAR *fromlen);
第一参数是套接字
第二参数是接收数据的空间
第三参数是接收数据的空间大小
第四参数是调用方式,一般为0
第五参数是地址结构体,用于保存发送数据方的地址信息
第六参数数地址结构体大小,必须初始化
*/
#include <WINSOCK2.H>
#include <iostream>
using namespace std;
#pragma comment(lib,"Ws2_32.lib") //连接库文件
int main()
{
WSADATA wsaData;
//加载成功则会返回0
if (WSAStartup(MAKEWORD(2,2),&wsaData))
{
cout << "加载失败" << endl;
return 0;
}
//创建数据报套接字,UDP的使用数据报套接字
SOCKET SerSocket = socket(AF_INET,SOCK_DGRAM,0);
if (SerSocket == INVALID_SOCKET)
{
cout << "创建套接字失败" << endl;
WSACleanup();
return 0;
}
//绑定
SOCKADDR_IN ServAddr;
ServAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
ServAddr.sin_family = AF_INET;
ServAddr.sin_port = htons(6100);
bind(SerSocket,(SOCKADDR*)&ServAddr,sizeof(SOCKADDR));
//接收数据,注意使用的是 recvfrom()函数
SOCKADDR_IN ClientAddr;
int AddrLen = sizeof(SOCKADDR);
char recvBuff[100];
memset(recvBuff,0,100);
recvfrom(SerSocket,recvBuff,sizeof(recvBuff),0,(SOCKADDR*)&ClientAddr,&AddrLen);
cout << recvBuff << endl;
//服务器端发送数据
sendto(SerSocket,"hello,这是服务器端发的消息",sizeof("hello,这是服务器端发的消息")+1,0,
(SOCKADDR*)&ClientAddr,sizeof(SOCKADDR));
closesocket(SerSocket);
WSACleanup();
return 0;
}
UDP客户端代码
//UDP网络编程,客户端
//int sendto(SOCKET s, const char FAR *buf, int len,int flags,const struct sockaddr FAR *to, int tolen );
//这个函数的用法和recvfrom()函数用法一样
#include <WINSOCK2.H>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib") //连接库文件
int main()
{
WSADATA wsaData;
//加载成功则会返回0
if (WSAStartup(MAKEWORD(2,2),&wsaData))
{
cout << "加载失败" << endl;
return 0;
}
//创建数据报套接字,UDP的使用数据报套接字
SOCKET ClientSocket = socket(AF_INET,SOCK_DGRAM,0);
if (ClientSocket == INVALID_SOCKET)
{
cout << "创建套接字失败" << endl;
WSACleanup();
return 0;
}
//客户端发送数据
SOCKADDR_IN ServAddr;//服务器地址信息
int Addrlen = sizeof(SOCKADDR);
ServAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
ServAddr.sin_port = htons(6100);
ServAddr.sin_family = AF_INET;
sendto(ClientSocket,"hello,这是客户端发的消息",sizeof("hello,这是客户端发的消息")+1,
0,(SOCKADDR*)&ServAddr,sizeof(SOCKADDR));
//客户端接收数据
char reBuff[100];
int len = sizeof(SOCKADDR);
recvfrom(ClientSocket,reBuff,sizeof(reBuff),0,(SOCKADDR*)&ServAddr,&len);
cout << reBuff << endl;
closesocket(ClientSocket);
WSACleanup();
return 0;
}
这部分的学习参考了孙鑫VC++编程的视频,部分内容来自视频的PPT内容.....