一. 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;
- }