以下内容源于朱有鹏嵌入式课程的学习整理,如有侵权,请告知删除。
目录
一、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
九、代码
(1)OSI 7层模型;
(2)网络为什么要分层?
(3)网络分层的具体表现。
参阅博客:http://www.cnblogs.com/BlueTzar/articles/811160.html
(1)TCP/IP协议是用的最多的网络协议实现;
(2)TCP/IP分为4层,对应OSI的7层;
(3)编程时最关注应用层,了解传输层,网际互联层和网络接入层不用管。
(1)CS架构介绍(client server,客户端服务器架构);
(2)BS架构介绍(broswer server,浏览器服务器架构);
(1)TCP协议工作在传输层,对上服务socket接口(与网络通信有关的API),对下调用IP层;
(2)TCP协议面向连接,通信前必须先3次握手建立连接关系后才能开始通信;
(3)TCP协议提供可靠传输,有反馈机制,不怕丢包、乱序等。
(1)TCP在传输有效信息前,要求通信双方必须先握手,建立连接才能通信;
(2)TCP的接收方收到数据包后会ack给发送方,若发送方未收到ack会丢包重传;
(3)TCP的有效数据内容会附带校验,以防止内容在传递过程中损坏;
(4)TCP会根据网络带宽来自动调节适配速率(滑动窗口技术);
(5)发送方会给各分割报文编号,接收方会校验编号,一旦顺序错误即会重传。
(7)这些都是别人已经实现的功能,我们只需知道这些特性就可以了,编程时不会在意这些细节。
(1)建立连接需要三次握手;
(2)建立连接的条件:服务器处于listen状态时(比如服务器在维护时,就不处在监听状态),客户端主动发起connect;
(1)关闭连接需要四次握手;
(2)服务器或者客户端都可以主动发起关闭;
(4)这些握手协议已经封装在TCP协议内部,socket编程接口平时不用管。
(1)具有公网IP地址的服务器(或者使用动态IP地址映射技术);
(2)服务器端socket、bind、listen后处于监听状态;
(3)客户端socket后,直接connect去发起连接。
(4)服务器收到并同意客户端接入后,会建立TCP连接,然后双方开始收发数据,收发时是双向的,而且双方均可发起。
(5)双方均可发起关闭连接。
(1)http、ftp;
(2)QQ服务器;
(3)mail服务器;
(1)socket
(2)bind
(3)listen
(4)connect
(1)send和write:发送的时候可以使用write,也可以使用send,只是send多了一个flag。
(2)recv和read:和上述一样。
用来进行ip地址转换的,点分十进制和二进制之间转换
(1)inet_aton、inet_addr、inet_ntoa(以前使用的,不支持IPV6)
(2)inet_ntop、inet_pton(现在推荐使用的,支持IPV6/4)
(1)都定义在 netinet/in.h;
(2)struct sockaddr
(3)struct sockaddr_in
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
1、inet_addr、inet_ntoa、inet_aton;(只能IPV4)
2、inet_pton、inet_ntop(可以用于IPV6)
结合代码来看过程、参数含义
(1)socket
(2)bind
(3)listen
(4)accept,返回值是一个fd
(1)socket
(2)connect
1、客户端发送&服务器接收
2、服务器发送&客户端接收
3、探讨:如何让服务器和客户端好好沟通
(1)客户端和服务器原则上都可以任意的发和收,但是实际上双方必须配合:client发的时候server就收,而server发的时候client就收;
(2)必须了解到的一点:client和server之间的通信是异步的,这就是问题的根源
(3)解决方案:依靠应用层协议来解决,即server和client事先做好一系列的通信约定。
1、自定义应用层协议第一步:规定发送和接收方法
(1)规定连接建立后,由客户端主动向服务器发出1个请求数据包,然后服务器收到数据包后回复客户端一个回应数据包,这就是一个通信回合;
(2)整个连接的通信就是由N多个回合组成的。
2、自定义应用层协议第二步:定义数据包格式。
3、常用应用层协议:http、ftp……
4、UDP简介
(1)将下面的这两份代码复制到linux下,形成两份文档client.c和server.c,然后gcc编译
(2)修改linux的ip地址为192.168.1.141
(3)先运行server.c生成的可执行文件,再运行client.c生成的可执行文件。
服务器
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;
}