网络编程学习日记一 -------OSI模型、TCP/IP协议模型、socket/IP地址/端口号、不同系统下网卡IP配置、服务器和客户端的配置代码

一、网络编程学习大纲。
1、网络体系结构模型。
2、专业术语:socket/IP地址/端口号。
3、网络编程通信协议:TCP协议/UDP协议。
4、IO模型:阻塞IO/非阻塞IO/多路复用/信号驱动。
5、超时接收数据的方法。

二、网络体系结构模型。
1、 模型有哪些?
一共有两种模型,分别是OSI模型(旧)、TCP/IP协议模型(新)。

2、这些模型是干嘛的?
只要系统能进行网络通信,那么系统内部肯定存在一个网络体系结构模型。
详细参考: 网络体系结构模型是干嘛的.jpg

3、OSI模型。
该模型一共有7层。
详细参考: OSI模型.jpg

4、TCP/IP协议模型。
该模型一共有4层。
详细参考: TCP-IP协议模型.jpg

三、专业术语:socket/IP地址/端口号。
1、什么是socket?
1)从英文角度分析:
socket在英文中被翻译为插座、套接字。 
那么问题来了,网络编程也称之为socket编程,为什么网络编程叫插座编程?
详细参考:为什么网络编程叫socket编程.jpg

2)从函数接口角度分析:
socket其实也是一个函数接口,其作用是申请对应协议的套接字(两台相同协议的主机之间通信的桥梁)。
例如:
TCP套接字(TCP socket) = socket(TCP协议);


UDP套接字(UDP socket) = socket(UDP协议);

套接字是一个特殊的文件描述符,为什么说是特殊的?
普通文件描述符 = open();
套接字 = socket();    -> 因为套接字不是通过open得到的,所以说是特殊的文件描述符。

2、什么是IP地址?
1)含义。
例如: "192.168.19.10"   --> 这种形态叫点分制。.
每一个IP地址都是32位,如果大家在网络编程中需要使用IP地址,那么就一定要将这32位转化为网络字节。

2)如何分配IP地址?
我们现在的课室是19课室,大家都是连在同一个路由器上,全班同学的IP地址都是"192.168.19.xx"。
中午,助教给每位同学分配三个IP地址,例如:给老关分配了2/3/4这三个数字,这三个IP地址,分别是给windows、linux、开发板。
例如,老关决定这样分配:
windows: 192.168.19.2
linux:   192.168.19.3
开发板:  192.168.19.4

1>. 如何配置windows的IP地址?
右键选择图标  --> 打开"网络与internet"设置   --> 以太网  -->  更改适配器选项  -->  双击"以太网"  -->   属性  ->  IPv4    -> 不要自动获取
按照以下的参数来配置:
IP地址:   192.168.19.x(以老关为例子,就写2)
子网掩码: 255.255.255.0   -> 照抄
默认网关: 192.168.19.1    -> 照抄
首选DNS:  202.96.134.133
备用DNS:  114.114.114.114

2>. 如何配置Ubuntu的IP地址?
=================================临时配置(重启虚拟机无效)=============================

第一步: 先查看一下当前IP地址是多少?
gec@ubuntu:~$ ifconfig
//以太网卡名称
ens33          Link encap:Ethernet  HWaddr 00:0c:29:60:a8:c3  
               UP BROADCAST MULTICAST  MTU:1500  Metric:1    -> 当前还没有设置IP地址。

第二步: 直接在终端输入命令即可。
gec@ubuntu:~$ sudo ifconfig ens33 192.168.19.3
[sudo] password for gec:

第三步:再次查看IP地址。
gec@ubuntu:~$ ifconfig
ens33     Link encap:Ethernet  HWaddr 00:0c:29:60:a8:c3  
          inet addr:192.168.19.3  Bcast:192.168.19.255  Mask:255.255.255.0   -> 临时设置成功。

=================================永久配置(重启虚拟机依然有效)=============================

第一步: 先查看一下当前IP地址是多少?
gec@ubuntu:~$ ifconfig
//以太网卡名称
ens33          Link encap:Ethernet  HWaddr 00:0c:29:60:a8:c3  
               UP BROADCAST MULTICAST  MTU:1500  Metric:1    -> 当前还没有设置IP地址。

第二步:打开配置IP地址的文件。
文件路径: /etc/network/interfaces

gec@ubuntu:~$ sudo chmod 777 /etc/network/interfaces
gec@ubuntu:~$ vi /etc/network/interfaces

--------------打开文件之后,原本的内容--------------------
  1 # interfaces(5) file used by ifup(8) and ifdown(8)                                                                                                                 
  2 auto lo
  3 iface lo inet loopback

---------------------------------------------------------

在这基础上,添加以下的内容即可:
auto ens33
iface ens33 inet static

address 192.168.19.x (以老关为例,就写3)
netmask 255.255.255.0
gateway 192.168.19.1

--------------添加完之后,文件的内容就要变成这样--------------
  1 # interfaces(5) file used by ifup(8) and ifdown(8)
  2 auto lo
  3 iface lo inet loopback
  4                                                                                                                                                                    
  5 auto ens33
  6 iface ens33 inet static
  7 
  8 address 192.168.19.3   --> 不要照抄我的,自己是什么数字,就写什么数字。
  9 netmask 255.255.255.0
 10 gateway 192.168.19.1
-------------------------------------------------------------

第三步:保存退出。
第四步:重启虚拟机。
第五步:再次查看IP地址,依然还在。
gec@ubuntu:~$ ifconfig
ens33     Link encap:Ethernet  HWaddr 00:0c:29:60:a8:c3  
          inet addr:192.168.19.3  Bcast:192.168.19.255  Mask:255.255.255.0


3>. 如何配置开发板的IP地址?
=================================临时配置(重启开发板无效)=============================

第一步: 先查看一下当前IP地址是多少?
[root@GEC6818 /]#ifconfig
以太网卡名称
eth0      Link encap:Ethernet  HWaddr F6:66:0D:B4:03:76  
          inet addr:192.168.20.3  Bcast:192.168.20.255  Mask:255.255.255.0    --> 之前设置过了,也没有关系。

第二步: 直接在终端输入命令即可。
[root@GEC6818 /]#ifconfig eth0 192.168.19.4

第三步:再次查看IP地址。
[root@GEC6818 /]#ifconfig
eth0      Link encap:Ethernet  HWaddr F6:66:0D:B4:03:76  
          inet addr:192.168.19.4  Bcast:192.168.19.255  Mask:255.255.255.0

==================================永久设置(重启开发板之后,IP地址依然有效)===========================

第一步: 先查看一下当前IP地址是多少?
[root@GEC6818 /]#ifconfig
以太网卡名称
eth0      Link encap:Ethernet  HWaddr F6:66:0D:B4:03:76  
          inet addr:192.168.20.3  Bcast:192.168.20.255  Mask:255.255.255.0    --> 之前设置过了,也没有关系。

第二步: 打开开发板的脚本文件。
脚本文件: /etc/profile    -> 只要板子一开启,就会默认执行这个文件。
[root@GEC6818 /]#vi /etc/profile

在该文件末尾添加一下的内容:
ifconfig eth0 192.168.19.4

第三步: 保存退出。
第四步: 重启板子,再次查看IP地址
[root@GEC6818 /]#ifconfig
eth0      Link encap:Ethernet  HWaddr F6:66:0D:B4:03:76  
          inet addr:192.168.19.4  Bcast:192.168.19.255  Mask:255.255.255.0   -> 无论重启多少次,IP地址依然有效。


3、什么是端口号?
如果某一个地址上,有一个进程在通信,假设这个进程内部有多个socket套接字,那么怎么知道跟谁通信呢?
解决方案: 通信时,除了确定IP地址之外,还要确定端口号。

两个进程之间需要通信,除了IP地址需要在同一个网段之外,还需要有相同的端口号。
                 主机A         ----       主机B
     IP地址:  192.168.19.3            192.168.19.5     --> 必须处于相同网段。(前面三个数字一致,最后一个数字不一样)
     端口号:     5000                    5000          --> 必须一致。

端口号取值范围: 0~65535  (2的16次方)  -> 16位
每一个端口号都是16位,如果大家在网络编程中需要使用端口号,那么就一定要将这16位转化为网络字节。
系统占用了的端口号: 0~1023      (用户不可以使用)
用户有效的端口号:   1024~65535  (用户随便使用)  -> 用户使用完端口号之后,由于资源没有释放,至少等待1分钟,才可以再次使用。


四、网络编程通信协议。
1、什么是通信协议?
两个进程之间使用网络编程来通信之前,必须定义好一个关系。   -> 协议:传输层协议。
主机A使用TCP协议,那么主机B就必须使用TCP协议才可以跟主机A通信。

2、传输层协议有哪些?
分别是TCP协议与UDP协议,两者区别是:
TCP协议  -->  Tranmission Control Protocol    这种协议特点: 面向于一种有连接的通信。 (打电话)
        传输       控制    协议

UDP协议  -->    User    Data    Protocol      这种协议特点: 面向于一种无连接的通信。 (寄信)
        用户   数据包     协议

3、使用TCP协议来通信,服务器与客户端的设计流程是如何?
客户端: 一般用于发送数据。

服务器: 一般用于接收数据,然后根据接受到的数据,然后回发对应的内容给客户端。
详细步骤参考: TCP协议的设计流程.jpg

 

服务器提供自己的IP地址可以使用一个宏:  INADDR_ANY
宏的路径在: /usr/include/linux/in.h

/* Address to accept any incoming messages. */
#define    INADDR_ANY        ((unsigned long int) 0x00000000)


服务器:

#include "head.h"
void *rcv_fun(void *arg)
{
	int connfd = *(int *)arg;
	//5. 不断从TCP协议中接受数据。
	char buf[100];
	while (1)
	{
		bzero(buf, sizeof(buf));
		recv(connfd, buf, sizeof(buf), 0);
		printf("from client:%s", buf);

		if (strncmp(buf, "quit", 4) == 0)
		{
			exit(0);
		}
	}

	//6. 挂断电话。
	close(connfd);
}
void *snd_fun(void *arg)
{
	int connfd = *(int *)arg;
	//5. 不断从TCP协议中接受数据。
	char buf[100];
	while (1)
	{

		fgets(buf, sizeof(buf), stdin);
		send(connfd, buf, strlen(buf), 0);

		if (strncmp(buf, "quit", 4) == 0)
		{
			exit(0);
		}
	}

	//6. 挂断电话。
	close(connfd);
}

int main(int argc, char *argv[]) //  ./Rose 50002
{

	//1. 创建TCP套接字。
	int sockfd;
	sockfd = socket(AF_INET, SOCK_STREAM, 0); //sockfd -> 未连接套接字

	//2. 绑定IP地址、端口号、协议到TCP套接字中。
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr, len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[1]));
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	bind(sockfd, (struct sockaddr *)&srvaddr, len);

	//3. 设置铃声。
	//sockfd -> 未连接套接字
	listen(sockfd, 5);
	//sockfd -> 监听套接字

	//4. 阻塞地坐等对方的连接。
	struct sockaddr_in cliaddr;
	bzero(&cliaddr, sizeof(cliaddr));

	int connfd;
	connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
	if (connfd > 0)
	{
		printf("new connection: %s\n", inet_ntoa(cliaddr.sin_addr));
	}
	//创建一个线程
	pthread_t tid1, tid2;
	pthread_create(&tid1, NULL, snd_fun, (void *)&connfd);
	pthread_create(&tid2, NULL, rcv_fun, (void *)&connfd);

	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
	close(sockfd);

	return 0;
}

客户端:

#include "head.h"
void*rcv_fun(void*arg){
	int sockfd=*(int*)arg;
		//5. 不断从TCP协议中接受数据。
	char buf[100];
	while(1)
	{
		bzero(buf,sizeof(buf));
		recv(sockfd,buf,sizeof(buf),0);
		printf("from client:%s",buf);

		if(strncmp(buf,"quit",4) == 0)
		{
			exit(0);
		}
	}

	//6. 挂断电话。
	close(sockfd);


	
}
void*snd_fun(void*arg){
		int sockfd=*(int*)arg;
		//5. 不断从TCP协议中接受数据。
	char buf[100];
	while(1)
	{


		fgets(buf,sizeof(buf),stdin);
		send(sockfd,buf,strlen(buf),0);

		if(strncmp(buf,"quit",4) == 0)
		{
			exit(0);
		}

	}

	//6. 挂断电话。
	close(sockfd);
	
}



int main(int argc, char *argv[]) //  ./Jack 192.168.19.5 50001
{

	//1. 创建TCP协议套接字。
	int sockfd;
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	//2. 打电话。
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr, len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[2]));
	inet_pton(AF_INET, argv[1], &srvaddr.sin_addr);

	int ret = connect(sockfd, (struct sockaddr *)&srvaddr, len);

	if (ret == -1)
	{
		printf("connect error!\n");
	}

	//创建一个线程
	pthread_t tid1, tid2;
	pthread_create(&tid1, NULL, snd_fun, (void *)&sockfd);
	pthread_create(&tid2, NULL, rcv_fun, (void *)&sockfd);

	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
	close(sockfd);


	return 0;
}

五、三次握手。

1、“三次握手”解析什么问题? 解析基于有连接的TCP协议是如何确定可靠性。

2、三次握手分别做了什么事情?

第一次握手: 客户端会发送syn包(seq=j)给服务器,然后等待服务器确定。

第二次握手: 服务器收到syn包之后,必须确定客户端的syn包(ack=seq+1),同时服务器在给客户端发送一个syn包(seq=k),此时服务器再处于等待的状态。

第三次握手: 客户端收到服务器的syn+ack包之后,向服务器发送确认包ack包(ack=seq+1),此包发送成功之后,客户端与服务器才会进入连接状态。

 


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值