您的位置:首页 > 理论基础 > 计算机网络

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

好了,废话不多说,现在开始我们的学习。

第一章、基本概念

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:读入记录长度,根据缓冲区是否能装下整条记录分成两种情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tcp ip c 编程 教程