tcp/ip 编程之基本概念

来源:互联网 发布:道路工程软件 编辑:程序博客网 时间:2024/06/04 23:30

首先标记别人写的一个较精炼的入门博客:http://blog.csdn.net/chocolate001/article/details/6612201

注:本章笔记对应书籍《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 5051class 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 1024class 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 1024class 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:读入记录长度,根据缓冲区是否能装下整条记录分成两种情况。



原创粉丝点击