tcp/ip 编程之基本概念
2017-05-21 13:58
477 查看
首先标记别人写的一个较精炼的入门博客:http://blog.csdn.net/chocolate001/article/details/6612201
注:本章笔记对应书籍《TCP/IP高效编程-改善网络程序的44个技巧》 作者:【美】Jon C.Snader
电子版:http://book.51cto.com/art/201104/253696.htm
好了,废话不多说,现在开始我们的学习。
第一章、基本概念
~使用面向连接的协议就像打电话。
应用程序通常都会进行长期的对话。记住这些状态,协议就可以提供可靠的传输。比如,发送端可以记住哪些数据已经发送出去了但还未被确认,以及数据是什么时候发送的。如果在某段时间间隔内没有收到确认,发送端可以重传数据。接收端可以记住已经收到了哪些数据,并将重复的数据丢弃。如果分组不是按序到达的,接收端可以将其保存下来,直到逻辑上先于它的分组到达为止。
传送数据过程:
第一阶段,在对等实体间建立连接。接下来是数据传输阶段,在这个阶段中,数据在对等实体间传输。最后,当对等实体完成数据传输时,连接被拆除。
可靠协议
无连接:
~使用无连接协议就像寄信。
数据会进行分组,分组被称为数据报。每个数据报都是一个独立的实体,与在两个相同的对等实体之间传送的任何其他数据报都没有关系。网络会尽最大努力传送每一个数据报,但并不保证数据报不丢失、不延迟或者不错序传输。
不可靠协议。
子网是因为AB类ip地址主机id部分过长,容易造成浪费。可以利用子网掩码将其中一部分划出作为子网。提高ip地址的利用率。CIDR恰好相反,由于C累地址网络ID部分过长,可容纳主机数量过少。因此利用网络掩码将网络id缩短,一部分用作主机id。
详细概念:http://www.cnblogs.com/nzbbody/p/6389587.html http://blog.csdn.net/dan15188387481/article/details/49873923
10.0.0.0--10.255.255.255 (前缀10/8);
172.16.0.0--172.31.255.255(前缀172.16/12);
192.168.0.0--192.168.255.255(前缀192.168/16)。
网络使用这三块地址中的任意一块时,网络中的任意一台主机都可以访问此网络中的所有其他主机,而无须担心与全局分配的IP地址产生冲突
但如果网络和外部相连:就要使用NAT
NAT负责在私有网络地址和一个或多个全局分配的IP地址之间进行翻译。
详细见:http://book.51cto.com/art/201104/253739.htm
vc6.0亲测通过。
tcp server框架:
tcp client框架:
UDP server框架
udp client框架
注:本章笔记对应书籍《TCP/IP高效编程-改善网络程序的44个技巧》 作者:【美】Jon C.Snader
电子版:http://book.51cto.com/art/201104/253696.htm
好了,废话不多说,现在开始我们的学习。
第一章、基本概念
1.面向连接与无连接协议的区别
面向连接:~使用面向连接的协议就像打电话。
应用程序通常都会进行长期的对话。记住这些状态,协议就可以提供可靠的传输。比如,发送端可以记住哪些数据已经发送出去了但还未被确认,以及数据是什么时候发送的。如果在某段时间间隔内没有收到确认,发送端可以重传数据。接收端可以记住已经收到了哪些数据,并将重复的数据丢弃。如果分组不是按序到达的,接收端可以将其保存下来,直到逻辑上先于它的分组到达为止。
传送数据过程:
第一阶段,在对等实体间建立连接。接下来是数据传输阶段,在这个阶段中,数据在对等实体间传输。最后,当对等实体完成数据传输时,连接被拆除。
可靠协议
无连接:
~使用无连接协议就像寄信。
数据会进行分组,分组被称为数据报。每个数据报都是一个独立的实体,与在两个相同的对等实体之间传送的任何其他数据报都没有关系。网络会尽最大努力传送每一个数据报,但并不保证数据报不丢失、不延迟或者不错序传输。
不可靠协议。
2.子网与CIDR概念
简单说,子网是因为AB类ip地址主机id部分过长,容易造成浪费。可以利用子网掩码将其中一部分划出作为子网。提高ip地址的利用率。CIDR恰好相反,由于C累地址网络ID部分过长,可容纳主机数量过少。因此利用网络掩码将网络id缩短,一部分用作主机id。
详细概念:http://www.cnblogs.com/nzbbody/p/6389587.html http://blog.csdn.net/dan15188387481/article/details/49873923
3.私有地址与NAT
三块永远不会被分配的保留IP地址。这三个地址块是:10.0.0.0--10.255.255.255 (前缀10/8);
172.16.0.0--172.31.255.255(前缀172.16/12);
192.168.0.0--192.168.255.255(前缀192.168/16)。
网络使用这三块地址中的任意一块时,网络中的任意一台主机都可以访问此网络中的所有其他主机,而无须担心与全局分配的IP地址产生冲突
但如果网络和外部相连:就要使用NAT
NAT负责在私有网络地址和一个或多个全局分配的IP地址之间进行翻译。
详细见:http://book.51cto.com/art/201104/253739.htm
4.开发并使用应用程序框架
socket程序中有很多固定的代码,博主将其修改整理在下面,编写程序时可直接套用。代码均为c语言,但为了结构清晰,用了c++的类结构。windows下vc6.0亲测通过。
tcp server框架:
#include<winsock2.h> #include <stdio.h> #include <stdlib.h> #pragma comment(lib, "ws2_32.lib") #define DEFAULT_PORT 5051 class Server { int iPort; WSADATA wsaData; SOCKET sListen, sAccept; int iLen, iSend; char sendbuf[100]; char revcbuf[100]; struct sockaddr_in ser, cli; public: int init(); void getdata(); }; int Server::init() { iPort = DEFAULT_PORT; strcpy(sendbuf,"hellow i am a sever"); if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to load Winsock.\n"); return 0; } sListen = socket(AF_INET, SOCK_STREAM, 0); if (sListen == INVALID_SOCKET) { printf("SOCKET() Failed:%d\n", WSAGetLastError()); return 0; } ser.sin_family = AF_INET; ser.sin_port = htons(iPort); ser.sin_addr.S_un.S_addr =htonl(INADDR_ANY); //inet_addr("192.168.1.109") if (bind(sListen, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR) { printf("bind() failed:%d\n", WSAGetLastError()); return 0; } if (listen(sListen, 5) == SOCKET_ERROR) { printf("listen() failed:%d\n", WSAGetLastError); return 0; } iLen = sizeof(cli); return 1; } void Server::getdata() { while (1) { sAccept = accept(sListen, (struct sockaddr*)&cli, &iLen); printf("accept"); if (sAccept == INVALID_SOCKET) { printf("accept failed:%d\n", WSAGetLastError()); break; } printf("Accept client IP:[%s],port:[%d] \n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port)); iSend = send(sAccept, sendbuf, sizeof(sendbuf), 0); if (iSend == SOCKET_ERROR) { printf("send() failed:%d\n", WSAGetLastError()); break; } while(1){ int k=recv(sAccept,revcbuf,sizeof(revcbuf), 0); if(k<=0) break; if(strcmp(revcbuf,"end")!=0) printf("%s\n",revcbuf);//inet_ntoa(cli.sin_addr) } printf("client shutdown\n"); printf("............."); printf("Sever waitting\n"); closesocket(sAccept); } } void main() { Server ser; if(ser.init()==0) { printf("............."); printf("Sever init error\n"); } printf("............."); printf("Sever waitting\n"); ser.getdata(); }
tcp client框架:
#include <WinSock2.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") #define DEFAULE_PORT 5051 #define DATA_BUFFER 1024 class Client { WSADATA wsaData; SOCKET sClient; int iPort; INT iLen; char sendbuf[100]; char recvbuf[100]; struct sockaddr_in ser; public: int init(char *argv[]); void senddata(); void close(); }; int Client::init(char *argv[]) { iPort = DEFAULE_PORT; memset(recvbuf, 0, sizeof(recvbuf)); if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to load Winsock.\n"); return 0; } ser.sin_family = AF_INET; ser.sin_port = htons(iPort); ser.sin_addr.S_un.S_addr = inet_addr(argv[1]); sClient = socket(AF_INET, SOCK_STREAM, 0); if (sClient == INVALID_SOCKET) { printf("socket() Failed :%d\n", WSAGetLastError()); return 0; } return 1; } void Client::senddata() { if (connect(sClient, (struct sockaddr*)&ser, sizeof(ser)) == INVALID_SOCKET) { printf("connect() Failed :%d\n", WSAGetLastError()); return; } else { iLen = recv(sClient, recvbuf, sizeof(recvbuf), 0); if (iLen == 0) return; else if (iLen == SOCKET_ERROR) { printf("recv() Failed :%d\n", WSAGetLastError()); return; } printf("recv() data from server:%s\n", recvbuf); while(1) { sendbuf[0]='\0'; printf("myname:\n"); while(sendbuf[0]=='\0') {gets(sendbuf);} if(strcmp(sendbuf,"end")==0) break; send(sClient,sendbuf,sizeof(sendbuf),0); } } } void Client::close() { closesocket(sClient); WSACleanup(); system("pause"); } void main(int argc, char *argv[]) { if (argc < 2) { printf("Usage :client[server IP address]\n"); return; } Client cli; if(cli.init(argv)==0) printf("init fail()"); cli.senddata(); cli.close(); }
UDP server框架
#include<winsock2.h> #include <stdio.h> #include <stdlib.h> #pragma comment(lib, "ws2_32.lib") #define DEFAULT_PORT 5050 //定义通信端口号 #define BUFFER_LENGTH 1024 //传输数据缓冲大小 class Server { int iPort; WSADATA wsaData;//这个结构被用来存储被WSAStartup函数调用后返回的Windows Sockets数据 SOCKET sSocket; int iLen, iSend,iRecv;//客户地址,发送数据,接受数据长度 char recv_buf[BUFFER_LENGTH];//接受字符串 struct sockaddr_in ser, cli; public : int init(); void getdata(); void close(); }; int Server::init() { iPort=DEFAULT_PORT; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to load Winsock.\n"); return 0; } //sScoket初始化并绑定自身信息 sSocket = socket(AF_INET, SOCK_DGRAM, 0); if (sSocket == INVALID_SOCKET) { printf("SOCKET() Failed:%d\n", WSAGetLastError()); return 0; } ser.sin_family = AF_INET; ser.sin_port = htons(iPort); ser.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//inet_addr("192.168.1.109") if (bind(sSocket, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR) { printf("bind() failed:%d\n", WSAGetLastError()); return 0; } iLen = sizeof(cli); memset(recv_buf,0,sizeof(recv_buf));//将s所指向的某一块内存中的前n个 字节的内容全部设置为ch指定的ASCII值 return 1; } void Server::getdata() { while (1) { iRecv=recvfrom(sSocket,recv_buf,BUFFER_LENGTH,0,(SOCKADDR*)&cli,&iLen); if (iRecv==SOCKET_ERROR) { printf("recvfrom() failed:%d\n", WSAGetLastError()); break; } else if(iRecv==0) break; else { printf("Accept client IP:[%s],port:[%d] \n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port)); printf("%s\n",recv_buf); } } } void Server::close() { closesocket(sSocket); WSACleanup(); } void main() { Server ser; if(ser.init()==0) { printf(".........................\n"); printf("Server start error\n"); printf(".........................\n"); } printf(".........................\n"); printf("Sever waitting\n"); printf(".........................\n"); ser.getdata(); ser.close(); }
udp client框架
#include <WinSock2.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") #define DEFAULE_PORT 5050 #define DATA_BUFFER 1024 class Client { WSADATA wsaData; SOCKET sClient; int iPort; int iLen,iSend; char send_buf[DATA_BUFFER]; struct sockaddr_in ser; public: int init(char *argv[]); void senddata(); void close(); }; int Client::init(char *argv[]) { iPort=DEFAULE_PORT; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to load Winsock.\n"); return 0; } ser.sin_family = AF_INET; ser.sin_port = htons(iPort); ser.sin_addr.S_un.S_addr = inet_addr(argv[1]);// sClient = socket(AF_INET, SOCK_DGRAM, 0); if (sClient == INVALID_SOCKET) { printf("socket() Failed :%d\n", WSAGetLastError()); return 0; } iLen=sizeof(ser); return 1; } void Client::senddata() { while(1) { send_buf[0]='\0'; printf("myname:\n"); while(send_buf[0]=='\0') {gets(send_buf);} iSend=sendto(sClient,send_buf,sizeof(send_buf),0,(struct sockaddr*)&ser,iLen); if(strcmp(send_buf,"end")==0) break; if (iSend == SOCKET_ERROR) { printf("send() failed:%d\n", WSAGetLastError()); return; } else if(iSend==0) return; } } void Client::close() { closesocket(sClient); WSACleanup(); } void main(int argc, char *argv[]) { if (argc < 2) { printf("Usage :client[server IP address]\n"); return; } Client cli; cli.init(argv); cli.senddata(); cli.close(); }
5.Tcp是一种流协议
TCP是一种流协议(stream protocol),这意味着数据是以字节流的形式发给接收者的,没有固定的报文和报文边界的概念。 接收端读取tcp数据,无法预知在这一次读操作中会返回多少个字节。
详细内容请阅读:http://book.51cto.com/art/201104/253755.htm
对于材料中的几点理解:
1.readn()函数:接收定长报文,由于任意一次读操作返回的数据量不可预测,可将一次读取分为多次读取,cnt标记剩余未读取字节数,rc记录本次读取字节长度。
2.readvrec()函数:接收变长报文,reclen:读入记录长度,根据缓冲区是否能装下整条记录分成两种情况。
相关文章推荐
- 一、Linux网络编程-TCP/IP基础(一)ISO/OSI参考模型、TCP/IP四层模型、基本概念
- 黑马程序员--10.网络编程--02.【网络传输三要素在Java中的体现】【TCP和UDP概念】【Socket基本概念】
- TCP/IP中基本概念——三次握手,四次分手
- Linux Tcp/ip UDP基本概念(6.8)
- TCP/IP 详解卷一学习笔记(一):TCP/IP 协议基本概念
- TCP/IP(1)-基本概念
- tcp/ip基本概念
- 网络:基本概念之HTTP、TCP/IP、UDP,Socket
- TCP/IP 基本概念
- TCP IP学习笔记1:基本概念
- TCP IP学习笔记1:基本概念
- TCP/IP_Socket编程 - 基本套接字
- TCP/IP笔记(1)-基本概念
- 1.TCP/IP基本概念