OSI 模型本身不是网络体系结构的全部内容,它并未确切地描述用于各层的协议和服务,仅提出每一层应该做什么。不过OSI 已经为各层制定了标准,但并不是参考模型的一部分,而作为单独的国际标准公布的。
TCP/IP 是一组用于实现网络互连的通信协议。Internet 网络体系结构以TCP/IP 为核心。基于TCP/IP 的参考模型将协议分成四个层次,它们分别是:网络访问层、网际互联层、传输层(主机到主机)、和应用层。
TCP/IP是一个网络通信模型,以及一整个网络传输协议家族,是网际网络的基础通信架构。因为该协议家族的两个核心协议:TCP(传输控制协议)和IP(网际协议),为该家族中最早通过的标准。故此它常被通称为TCP/IP协议族(TCP/IP Protocol Suite),简称TCP/IP。
一. TCP/IP模型 :TCP/IP 是一组用于实现网络互连的通信协议。Internet 网络体系结构以 TCP/IP 为核心。基于TCP/IP 的参考模型将协议分成四个层次,它们分别是:网络访问层、网际互联层、传输层(主机到主机)、和应用层。
二. IP协议 :IP(Internet Protocol)协议是 TCP/IP 的核心协议。IP 协议(Internet Protocol)又称互联网协议,是支持网间互连的数据报协议。它提供网间连接的完善功能, 包括 IP 数据报规定互连网络范围内的 IP 地址格式。
目前的 IP 地址(IPv4:IP 第 4 版本)由 32 个二进制位表示,每 8 位二进制数为一个整数,中间由小数点间隔,如 159.226.41.98,整个 IP 地址空间有 4 组 8 位二进制数,由表示主机所在的网络的地址以及主机在该网络中的标识共同组成,它通常被分为A,B,C,D,E五类,其中商业应用只用到A,B,C三类1。
(1)TCP(传输控制协议)
TCP 是一种可靠的面向连接的传送服务。主机交换数据必须首先建立连接,传输完毕后断开连
接。它用位流通信,即数据被作为无结构的字节流。它提供反馈重发机制,从而保证数据的可靠传
输。
(2)三次握手
TCP 连接建立是一个三次握手过程,三次握手的目的是使数据的发送和接收同步。TCP 连接过程
如下图所示:
TCP 连接过程如下:
TCP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen();
5、接收客户端上来的连接,用函数accept();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
8、关闭监听;
TCP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
利用socket编程基础实现一个基础的文件传输程序,可以在不同设备之间传输文件,视频,图片,音乐等内容。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
#define SERVER_PORT 8000 //监听本机8000端口
#define MAX 4096
#define BUF_SIZE 10240
void empty_stdin() {
int c;
do {
c = getchar();
} while (c != '\n' && c != EOF);
}
int main(void)
{
struct sockaddr_in serveraddr,clientaddr;
int sockfd,addrlen,confd,len;
char ipstr[128];
char buf[16];
pid_t pid;
//1.socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
//2.bind
bzero(&serveraddr,sizeof(serveraddr));
//地址族协议ipv4
serveraddr.sin_family = AF_INET;
//ip地址
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERVER_PORT);
bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
//3.listen
listen(sockfd,20);//128作为可同时链接的数量上线
//4. accept阻塞监听客户端的链接请求
addrlen = sizeof(clientaddr);
confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen);
//如果有客户端连接上服务器,就输出客户端的ip地址和端口号
printf("client ip %s\tport %d\n",
inet_ntop(AF_INET,(struct sockaddr *)&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),ntohs(clientaddr.sin_port));
int flag=1;
while(flag){
char fdownload[100] = {0};
recv(confd,fdownload,100,0);//接收客户端的下载请求获得文件名
if(!strcmp(fdownload,"quit")) break;//客户端输入quit退出程序
else{
FILE *fp = fopen(fdownload, "rb"); //以二进制方式打开文件
if(fp == NULL){
printf("Cannot open file, press any key to exit!\n");
break;
}
struct stat statbuf; //这三行代码获得文件的大小,得到文件字节数
stat(fdownload,&statbuf);
int size=statbuf.st_size;
char t[20];
printf("%d \n",size);//文件大小
sprintf(t,"%d",size);//转换整数到字符型数据
//printf("start transfer\n");
send(confd,t,20,0);//把文件大小发给客户端
char buffer[BUF_SIZE] = {0}; //缓冲区
long nCount,mc=0;
long si,sj,m,c;
recv(confd,t,4,0);//获得开始传输指令
if(t[0]=='o'){
printf("start transfer\n");
/*一下内容开始发送文件内容 ,因为文件大小会超出发送缓冲区大小,因此在这里循环调用send()函数进行发送,每一次发送nCount个字节,传输缓冲区这里设置为10240(并不合理,但不想花脑子想这个问题)*/
while( (nCount = fread(buffer, 1, BUF_SIZE, fp)) > 0 ){
mc=mc+nCount;
si=mc*30/size;//这里开始计算传输比例,式字计算顺序不能更改,否则出现数据溢出,而产生错误。
m=si-sj;
printf("%*s|%d%%",30-si,"",(mc*100/size));//在固定位置打印百分数
printf("\r\033["); //退格
for(int t=0;t<si+1;t++)
{
printf(">");
setbuf(stdout, NULL);
}
send(confd, buffer, nCount, 0);
sj=si;
}
printf("transfer success!\n");
fclose(fp);
}
}
}
close(confd);
//close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define HELLO_WORLD_SERVER_PORT 6666
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
#define BUF_SIZE 10240
#define SERVER_PORT 8000
#define MAXLINE 4096
int main(void)
{
struct sockaddr_in serveraddr;
int confd,len;
char ipstr[] = "192.168.1.100";//这是服务器的地址,使用ifconfig来查看
char buf[MAXLINE];
//1.创建一个socket
confd = socket(AF_INET,SOCK_STREAM,0);
//2.初始化服务器地址,指明我要连接哪个服务器
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);
serveraddr.sin_port = htons(SERVER_PORT);
//3.链接服务器
connect(confd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
int flag=1,size;
char t[20];
memset(&t,0,sizeof(t));
while(flag){
char fdownload[100] = {0};
printf("Input filename to download: ");
gets(fdownload);
if(!strcmp(fdownload,"quit")) break;
send(confd,fdownload,100,0);
char filename[100] = {0}; //文件名
len=recv(confd,t,20,0);
size=atoi(t);
double sizel;
sizel=size/1048576.0;
printf("filesize; %.2f MB\n ",sizel);
printf("Input filename to save: ");
gets(filename);
if(!strcmp(filename,"quit")) break;
else{
FILE *fp = fopen(filename, "wb"); //以二进制方式打开(创建)文件
if(fp == NULL){
printf("Cannot open file, press any key to exit!\n");
break;
}
send(confd,"o",4,0);
char buffer[BUF_SIZE] = {0}; //文件缓冲区
long nCount,mc=0;
long si,sj,m,c;
printf("Start receive!\n");
while( (nCount = recv(confd, buffer, BUF_SIZE, 0)) > 0 ){
mc=mc+nCount;
si=mc*30/size;
m=si-sj;
printf("%*s|%d%%",30-si,"",(mc*100/size));
printf("\r\033["); //退格
for(int t=0;t<si+1;t++)
{
printf(">");
setbuf(stdout, NULL);
}
fwrite(buffer, nCount, 1, fp);
if(mc==size) break;
}
printf("Transfer success!\n");
fclose(fp);
}
}
close(confd);
return 0;
}
手机客户端传输截图,手机端安装"手机CAPP"
app即可运行客户端程序:
解决
:把输入缓冲区大小设置为零,这么一来可以加快打印效率。如果在windows下面可以使用fflush来清空缓冲区使得数据及时输出。关于IP地址的更多信息 ↩︎
这部分来自这位博主的博客 ↩︎