网络编程实战

以下内容源于朱有鹏嵌入式课程的学习整理,如有侵权,请告知删除。

目录

一、Linux网络编程框架

1、OSI参考模型

2、TCP/IP协议引入

3、BS和CS

二、TCP协议的学习1

1、关于TCP理解的重点

2、TCP如何保证可靠传输

三、TCP协议的学习2

1、TCP建立连接时的三次握手

2、TCP释放连接时的四次握手

3、基于TCP通信的服务模式(编程时也是按这个模式编写的)

4、常见的使用TCP协议的网络应用

四、socket编程接口(与网络通信相关的API)介绍

1、建立连接

2、发送和接收

3、辅助性函数

4、表示IP地址相关数据结构

五、IP地址格式转换函数实践

六、soekct实践编程1_2

1、服务器端程序编写

2、客户端程序编写

七、socket实践编程3

八、socket编程实践4

九、代码


 

一、Linux网络编程框架

1、OSI参考模型

(1)OSI 7层模型;

(2)网络为什么要分层?

(3)网络分层的具体表现。

2、TCP/IP协议引入

参阅博客:http://www.cnblogs.com/BlueTzar/articles/811160.html

(1)TCP/IP协议是用的最多的网络协议实现;

(2)TCP/IP分为4层,对应OSI的7层;

网络编程实战_第1张图片

(3)编程时最关注应用层,了解传输层,网际互联层和网络接入层不用管。

网络编程实战_第2张图片

网络编程实战_第3张图片

3、BS和CS

(1)CS架构介绍(client server,客户端服务器架构);

(2)BS架构介绍(broswer server,浏览器服务器架构);

 

二、TCP协议的学习1

1、关于TCP理解的重点

(1)TCP协议工作在传输层,对上服务socket接口(与网络通信有关的API),对下调用IP层;

(2)TCP协议面向连接,通信前必须先3次握手建立连接关系后才能开始通信;

(3)TCP协议提供可靠传输,有反馈机制,不怕丢包、乱序等。

2、TCP如何保证可靠传输

(1)TCP在传输有效信息前,要求通信双方必须先握手,建立连接才能通信

(2)TCP的接收方收到数据包后会ack给发送方,若发送方未收到ack会丢包重传

(3)TCP的有效数据内容会附带校验,以防止内容在传递过程中损坏;

(4)TCP会根据网络带宽来自动调节适配速率(滑动窗口技术);

(5)发送方会给各分割报文编号,接收方会校验编号,一旦顺序错误即会重传。

(7)这些都是别人已经实现的功能,我们只需知道这些特性就可以了,编程时不会在意这些细节。

 

三、TCP协议的学习2

1、TCP建立连接时的三次握手

(1)建立连接需要三次握手;

(2)建立连接的条件:服务器处于listen状态时(比如服务器在维护时,就不处在监听状态),客户端主动发起connect;

2、TCP释放连接时的四次握手

(1)关闭连接需要四次握手;

(2)服务器或者客户端都可以主动发起关闭;

(4)这些握手协议已经封装在TCP协议内部,socket编程接口平时不用管。

 

3、基于TCP通信的服务模式(编程时也是按这个模式编写的)

(1)具有公网IP地址的服务器(或者使用动态IP地址映射技术);

(2)服务器端socket、bind、listen后处于监听状态;

(3)客户端socket后,直接connect去发起连接。

(4)服务器收到并同意客户端接入后,会建立TCP连接,然后双方开始收发数据,收发时是双向的,而且双方均可发起。

(5)双方均可发起关闭连接。

 

4、常见的使用TCP协议的网络应用

(1)http、ftp;

(2)QQ服务器;

(3)mail服务器;

 

四、socket编程接口(与网络通信相关的API)介绍

1、建立连接

(1)socket

网络编程实战_第4张图片

网络编程实战_第5张图片

  • socket函数类似于open,用来打开一个网络连接,如果成功则返回一个网络文件描述符(int类型)。
  • 对网络连接进行操作,都是通过这个网络文件描述符进行:发送相当于写文件,接收相当于读文件。
  • 函数参数见上图标注,编程中的一个实例:socket(AF_INET,SOCK_STREAM,0)

(2)bind

(3)listen

(4)connect

网络编程实战_第6张图片

 

2、发送和接收

(1)send和write:发送的时候可以使用write,也可以使用send,只是send多了一个flag。

网络编程实战_第7张图片

(2)recv和read:和上述一样。

 

3、辅助性函数

用来进行ip地址转换的,点分十进制和二进制之间转换

(1)inet_aton、inet_addr、inet_ntoa(以前使用的,不支持IPV6)

(2)inet_ntop、inet_pton(现在推荐使用的,支持IPV6/4)

 

4、表示IP地址相关数据结构

(1)都定义在 netinet/in.h;

(2)struct sockaddr

  • 是linux的网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都需要这个结构体,兼容IPV4和IPV6的。
  • 在实际编程中,会被一个struct sockaddr_in或者一个struct sockaddr_in6所填充。即它是一个形参,传ruct sockaddr_in或者struct sockaddr_in6都可以。

(3)struct sockaddr_in

  • 注意里面包含了ip地址和端口号
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;                 /* Port number.  */
    struct in_addr sin_addr;            /* Internet address.  */


    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
  };

(4)struct in_addr

struct in_addr
{
    in_addr_t s_addr;
};

(5)typedef uint32_t in_addr_t

  • 网络内部用来表示IP地址;

 

五、IP地址格式转换函数实践

1、inet_addr、inet_ntoa、inet_aton;(只能IPV4)

 

网络编程实战_第8张图片

 

网络编程实战_第9张图片

  • 从上面可知,存在大小端问题。解决方法:网络字节序,其实就是统一使用大端模式。
  • 如果电脑使用的是小端模式,先把ip地址转换为大端模式;如果本来就是大端模式,那就不用改。
  • 这些API会自动完成这样的功能,即会检测所用的电脑的模式,然后在代码内部进行处理,最后输出一定是大端模式的。

2、inet_pton、inet_ntop(可以用于IPV6)

  • 例一

网络编程实战_第10张图片

网络编程实战_第11张图片

 

  • 例二

网络编程实战_第12张图片

 

六、soekct实践编程1_2

结合代码来看过程、参数含义

1、服务器端程序编写

(1)socket

(2)bind

(3)listen

(4)accept,返回值是一个fd

  • 如果accept正确返回,则表示服务器和客户端成功建立一个TCP连接
  • 可以通过该TCP连接与客户端进行读写操作。
  • 而读写操作需要一个fd,此fd由accept返回。
  • socket返回的fd叫做监听fd,是用来监听客户端的,不能用来和任何客户端进行读写;
  • accept返回的fd叫做连接fd,用来和客户端程序进行读写。

2、客户端程序编写

(1)socket

(2)connect

  • 端口号,实质就是一个数字编号,用来唯一地标识一个能上网的进程。
  • 端口号和IP地址,会被打包到当前进程发出的或者接收到的每个数据包。
  • 每个数据包将来在网络上传递的时候,内部都包含了发送方和接收方的信息(就是IP地址和端口号)。

 

七、socket实践编程3

1、客户端发送&服务器接收

2、服务器发送&客户端接收

3、探讨:如何让服务器和客户端好好沟通

(1)客户端和服务器原则上都可以任意的发和收,但是实际上双方必须配合:client发的时候server就收,而server发的时候client就收;

(2)必须了解到的一点:client和server之间的通信是异步的,这就是问题的根源

(3)解决方案:依靠应用层协议来解决,即server和client事先做好一系列的通信约定。

 

八、socket编程实践4

1、自定义应用层协议第一步:规定发送和接收方法

(1)规定连接建立后,由客户端主动向服务器发出1个请求数据包,然后服务器收到数据包后回复客户端一个回应数据包,这就是一个通信回合;

(2)整个连接的通信就是由N多个回合组成的。

2、自定义应用层协议第二步:定义数据包格式。

3、常用应用层协议:http、ftp……

4、UDP简介

 

九、代码

1、运行步骤

(1)将下面的这两份代码复制到linux下,形成两份文档client.c和server.c,然后gcc编译

(2)修改linux的ip地址为192.168.1.141

(3)先运行server.c生成的可执行文件,再运行client.c生成的可执行文件。

2、代码与逻辑

服务器

socket函数,获取网络连接的文件描述符
bind函数,将服务器的端口、ip地址与socket函数创建的文件描述符绑定
listen函数,监听服务器的当前端口(其他端口不监听)
accept函数,阻塞以等待用户连接

客服端
socket函数,获取网络连接的文件描述符
connect函数,连接服务器
---连接上之后--
send函数,客服端给服务器发送数据
recv函数,客服端接收服务器的回复

client

#include
#include
#include
#include
#include

#define  SERPORT 6013
#define  SERADDR "192.168.1.141"//要设置为服务器的IP地址
#define  CMD_REGISTER 1

#define  STAT_OK   0
#define  STAT_ERR  1

typedef struct FORMAT
{
	char name[20];
	int age;
	int cmd;
	int stat;
}info;

char sendbuf[100];
char recvbuf[100];

int main(void)
{
   //第一步:socket函数,获取网络连接的文件描述符
   int sockfd=-1;
   int ret=-1;
   struct sockaddr_in seraddr={0};
   
   
   sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(-1==sockfd)
   {
	   perror("socket error:");
	   return -1;
   }
   printf("socket success.socket=%d.\n",sockfd);
   
   //第二步:connect函数,连接服务器
   seraddr.sin_family=AF_INET;//定义ip类型,IPV4还是IPV6
   seraddr.sin_port=htons(SERPORT);
   seraddr.sin_addr.s_addr=inet_addr(SERADDR);
   ret=connect(sockfd,(const struct sockaddr*)&seraddr,sizeof(seraddr));
   printf("连接成功\n");

/*   
   while(1)
   { 
       //第三步:send函数,客户端给服务器发送信息
	   printf("请输入你想发给服务器的内容:\n");
	   scanf("%s",sendbuf);
	   ret=send(sockfd,sendbuf,strlen(sendbuff),0);
       
	   //第四步:客户端接收服务器端的回复
	   memset(recvbuf,0,sizeof(recvbuf));
	   ret=recv(sockfd,recvbuf,sizeof(recvbuf),0);
	   printf("服务器回复内容是:%s\n",recvbuf);
	  
	  //第五步:客户端解释服务器的回复,再做下一步定夺
   }
*/  
   info st1;
   while(1)
   { 
       	   printf("\n请输入学生姓名:\n");
	   scanf("%s",st1.name);
	  
           printf("\n请输入学生年龄:\n");
	   scanf("%d",&st1.age);
	   st1.cmd=CMD_REGISTER;
	  
           ret=send(sockfd,&st1,sizeof(info),0);
       
	   //第四步:客户端接收服务器端的回复
	   memset(&st1,0,sizeof(st1));
	   ret=recv(sockfd,&st1,sizeof(st1),0);
	   if(STAT_OK==st1.stat)
	   {
	      printf("注册学生信息成功!");
	   }
	   else
              printf("注册学生信息失败!");
   }
 
   return 0;
}

server:

#include 
#include 
#include 
#include           /* See NOTES */
#include 

#define  MYPORT    6013
#define  SERADDR   "192.168.1.141"//要设置为服务器的IP地址
#define  BLEN      100
#define  STAT_OK    0
#define  STAT_ERR   1
#define  CMD_REGISTER 1
typedef struct FORMAT
{
	char name[20];
	int age;
	int cmd;
	int stat;
}info;

char recvbuf[100];

int main(void)
{
   int sockfd=-1;
   int ret=-1;
   int clifd=-1;
   socklen_t len=0;
   
   struct sockaddr_in seraddr={0};//也可以使用memset函数进行初始化
   struct sockaddr_in clientaddr={0};
   
   
   //第一步:socket函数,获取网络连接的文件描述符
   sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(-1==sockfd)
   {
	   perror("socket error:");
	   return -1;
   }
   printf("socket success!socket=%d.\n",sockfd);
   
   //第二步:bind函数,绑定sockfd与服务器的ip地址、端口号
   //填充seraddr这个结构体
   seraddr.sin_family=AF_INET;//定义ip类型,IPV6还是IPV4
   seraddr.sin_port=htons(MYPORT);
   seraddr.sin_addr.s_addr=inet_addr(SERADDR);
   
   ret=bind(sockfd,(const struct sockaddr*)&seraddr,sizeof(seraddr));//类型不一样,会警告
   if(ret==-1)
   {
	   perror("bind error:");//函数本身有错误号时可以用perror来显示错误信息
   }
   printf("bind success!\n");
   
   //第三步:listen函数,监听服务器的当前端口(其他端口不监听)
   ret=listen(sockfd,BLEN);//第二个参数是服务器允许的队列长度,比如最多100号
   if(ret==-1)
   {
	   perror("listen error:");
   }
   printf("listen success!\n");

  //第四步:accept函数,阻塞等待用户连接  
  //注意accept返回值是网络文件描述符,和sockfd不同,它才是真正的用于发送数据的fd,而sockfd只是用于侦听的。
   clifd=accept(sockfd,(struct sockaddr*)&clientaddr,&len);
   printf("用户连接成功!\n");
   
/*  
  while(1)
   {
	   //第五步:recv函数,服务器开始接收数据
	   ret=recv(clifd,recvbuf,sizeof(recvbuf),0);
       printf("client发送的内容是:%s\n",recvbuf);
	   memset(recvbuf,0,sizeof(recvbuf));
	   
	   //第六步:服务器解释数据包
	   
	   //第七步:回复客户端OK
	   ret=send(clifd,"OK",2,0);
   }
*/
   
   while(1)
   {
	   info st;
	   ret=recv(clifd,&st,sizeof(recvbuf),0);
       if(CMD_REGISTER==st.cmd)
	   {
	      printf("用户要注册学生信息\n");
	      printf("姓名:%s,年龄:%d\n",st.name,st.age);
		  st.stat=STAT_OK;
		  ret=send(clifd,&st,sizeof(info),0);
	   }
   }
   return 0;
}

 

你可能感兴趣的:(系统与网络编程)