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以下的端口号保留给预定义的服务
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内容.....