网络编程基础小结

整理了几天了,都是空闲时间整理的,算是对网络编程基础的一个小小的总结,目的在于巩固一下。
网络编程概述

1,网络的作用:信息交换和资源共享。
2,如果没有网络,会怎样?
3,因特网、互联网和万维网之间的区别:凡是由能彼此通信的设备组成的网络就叫互联网(因特网是互联网中最大的一种)、因特网(万维网是其中的一种)、只要应用层使用的是HTTP协议,那么就称为万维网,即www开头的。目前围绕互联网发展业务的主要有两种
4 ,OSI协议参考模型(共七层由上到下): 应用层、表示层、会话层、传输层(设置数据格式)、网络层( 最主要的IP协议 )、数据链路层( 网络驱动属于该层 )、物理层 。( 屋里 有些 数据 通过 网络传输 实现 会话 ,这 表示应用 了七层
5 ,TCP/IP参考模型(由上到下) : 应用层、传输层、网络层、网络接口层
网络接口层:负责将二进制流转换成数据帧,并进行数据帧的发送和接受。要注意的是 数据帧是独立的网络信息传输单元
网络层:负责将数据帧封装成IP数据包,并运行必要的路由算法。网络层的作用是分组交换网在发送数据时将传输层产生的报文数据分割并封装成分组。网络层的另一个任务是给分组加上源IP地址和目的IP地址,从而使用分组能够通过网络中的路由器找到目的主机。
传输层:负责端对端之间的通信回话链接与建立。传输协议的选择根据数据传输方式而定。
应用层:负责应用程序的网络应访问,这里通过端口号来识别各个不同的进程。
支持万维网应用的HTTP协议、支持电子邮件的SMTP协议、支持文件传输的FTP协议等等。
6,TCP/IP协议族
应用层:telnet + fp 。传输层:TCP + UDP。网络层:ICMP+IGMP 、IPv6、IPv4。网络接口层:ARP、RARP、MPLS。
五层协议是相互包含的关系。
AR P :用于获得同一物理网络中的应建主机地址。
MPLS :多协议标签协议,是很有发展前景的下一代网络协议。
IP :负责在主机和网络之间寻址和路由数据包。
ICMP :由于发送有关数据包的传送错误协议。
IGMP :被IP主机用来向本地多路广播路由器报告主机组成员的协议。
TCP :为应用程序提供可靠地通信连接。适合于一次传输大批数据的情况。并适用于要求得到相应的应用程序。
UDP :提供了无线连接通信,且不对传送包进行可靠性保证。适合一次传输少量数据,可靠性则由应用层来负责。
7,传输介质:双绞线、同轴电缆、光纤。前两个传输电信号、光纤传输光信号。
8,按照方式划分为: 电路交换 报文交换 分组交换
电路交换:建立一条通路,同一时刻只能传输此信息。
报文交换:结点把要发送的信息组织成数据包,就是报文,断点发送。
9,按服务方式划分为 C/S P2P
C/S模式就是“客户机—服务器”模式,这种模式是平时用的最多的一种模式。这个模式有很多客户端,但只有一个服务器。如树形分散。
P2P模式,即对等模式。对等模式就是每一台电脑和其它电脑都是对等的。你的电脑可以提供资源给我,我的电脑也可以提供资源给你。如网形互联。
10,通常应用程序通过打开一个socket来使用TCP服务,TCP管理到其他socket的数据传递。可以说通过IP的源/目的可以唯一的区分网路中的两个设备的链接,通过socket的源/目的可以唯一的区分网络中的两个应用程序的链接。
11,TCP对话通过 三次握手协议 来进行初始化。三次握手的目的是使数据段的发送和接收同步,告诉其他主机一次可接收的数据量,并建立虚链接。
12,TCP即传输控制协议,实体所采用的基本协议是滑动窗口协议。
13,如果发送方的定时器在确认信息到达前超时,则会重新发送该数据包。
14,UDP即用户数据报协议,是一种无连接协议,不需要三次握手来建立链接。比TCP协议更高效。
15,协议的选择:
(1)对数据可靠性的要求:应选择TCP
(2)应用的实时性:选择UDP
(3)网络的可靠性:网络良好用UDP,不好用UDP。
sudo apt-get install wireshark
安装Wireshark
sudo gedit /usr/share/wireshark/init.lua
dofile(DATA_DR.."console.lua")前面加上“__”

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。
DNS是UDP协议的一种。
函数部分

1,ifconfig用于查询IP地址
2,创建套接字
int socket(int domain, int type, int protocol);
domain:IP类型/包括IPv4和IPv6【由于目前只支持IPv4的地址所以只能写AF_INET】
type:套接字类型/可靠并有连接的——(字节流套接字)SOCK_STREAM;不可靠的无连接的——(数据报套接字)SOCK_DGRAM;(原始套接字)SOCK_RAW
protocol:指定协议,通常是默认协议写 0 (原始套接字除外)
返回值:成功返回非负的套接字描述符。出错返回-1。
int sockfd; //存放创建的套接字的描述符
if (-1 == (sockfd=socket(AF_INET, SOCK_STREAM, 0))) /*IPv4协议族,IP类型为SOCK_STREAM*/
{
perror("Socket error");
exit(-1);
}
3,字节序
大端:高位字节放低地址。小端:高位字节放高地址。
比如32位的整数0x01234567,从地址0x100开始存,大端和小端的存放顺序如:
比如位置从高位到低位是:0x100、0x1010x1020x103
(1) 大端字节序:0x100(01)、0x101(23)、0x102(45)、0x103(67)

(2) 小端字节序:0x100(67)、0x101(45)、0x102(23)、0x103(01)

一般而言,x86系列的CPU都是little endian的字节序,单片机一般是big endian的字节序。而Internet中数据的顺序是一定要统一的,一般来说是大端的,即big endian。所以当我们电脑的CPU不是大端时,内部字节存储顺序就和网络字节序不同,这时就要进行转换。那怎么判断我们的电脑是大端还是小端呢?下面的程序来判断。
思路:我们定义一个4字节的int型变量,并初始化0x01234567;然后将它强制转换成1字节的char型。转换后char默认保存的是低位的地址,此时如果该地址中的数据是0x67说明低位地址中放的是低字节数据,即小端
# include <stdio.h>
int main(void)
{
unsigned int a = 0x01234567;
unsigned char b = (unsigned char)a;

if (b == 0x67)
{
printf ("little endian\n");
}
else
{
printf ("big endian\n");
}
return 0;
}
4,字节序转换

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t即unsigned long型,uint16_t即unsigned short型。这两个函数很相似,都是hton开头。hton即host to net,即主机序转换成网络序。网络序肯定是大端的,而主机序就是你这台电脑的字节序,如果是大端的就相当于没转,如果是小端的就转换成大端的。这两个hton函数的返回值类型和参数类型是一样的,它们的返回值就是转换好的网络字节序。
两个重要的数据类型:sockaddr和sockaddr_in
①二者的作用都是用来保存socket信息。
②内容:
struct sockaddr
{
unsigned short sa_family;//地址族,是IPv4还是IPv6
char sa_data[14];//14节的协议地址,包含socket的IP地址和端口号。
};

struct sockaddr_in
{

short int sa_family;//地址族,一般写AF_INET。
unsigned short int sin_port;//网络字节序的端口号,为非负整数。
struct in_addr sin_addr;//网络字节序的IP地址,
unsigned char sin_zero[8]; //填充0以保持与struct sockaddr 同样大小
}
注意:端口号的范围是1~65535,但1~1024范围的端口已经被系统使用或保留,所以这个范围中的端口我们最好不要用。1024以后的端口理论上我们可以随便写,但一般10000以后的端口比较安全。
③这两个类型是等效的,可以相互转化, 通常sockaddr_in 数据类型使用更加方便。
④结构字段:sa_family字段可选的常见值。
头文件<netinet/in.h>
AF_INET:IPv4协议。
AF_INET6:IP6协议
AF_LOCAL:UNIX域协议
AF_LINK:链路地址协议
AF_KEY:密钥套接字
⑤存放IP地址的成员:sockaddr_in结构体中存放IP地址的成员struct in_addr sin_addr也是一个结构体
typedef struct in_addr
{
union
{
struct
{
unsigned char s_bl, s_b2, s_b3, s_b4;
}S_un_b;

struct
{
unsigned short s_wl, s_w2;
}S_un_w;

unsigned long s_addr;
}S_un;
}IN_ADDR;
它实际上是一个联合体,或者说是共用体。共用体和结构体的区别是:它可以使用几个不同类型的成员,但它们共占一段内存空间(相互覆盖),每次只有一个起作用。其目的是让我们选择IP地址的形式。上述成员中第一个就是常见的IP地址,即“*.*.*.*.”这种点分十进制形式的。第二个成员就是每两个十进制用一个变量保存。这两个都比较麻烦,所以我们一般都用第三种形式,即将点分十进制形式的IP地址转换成一个32位的非负的长整数,然后赋给s_addr这个成员。问题来了,如何将一个点分十进制形式的IP地址转换成一个32位的unsigned long型整数?
5,inet_addr( )函数,功能就是解决上面的问题,将点分十进制的IP地址,比如192.168.222.199这种形式的IP地址转换成一个32位网络字节序的无符号长整数。
头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
返回值:转换好的unsigned long 型的网络字节序。
用法:只需要将返回的这个长整数赋给sin_addr结构体中的s_addr成员即可,这样IP地址就设置完毕了。
如:
struct sockaddr_in server_addr;
server_addr.sin_addr.s_addr = inet_addr("192.168.222.199");
此外,我们写程序时一般服务器和客户端都是我们的电脑,IP地址也可以写成127.0.0.1(本地主机的IP地址)。
6,绑定函数:bind()
服务器和客户端进行通信就是通过它们连接在一起的套接字。
功能:将套接字与 本地 IP地址(如果是在服务器中使用bind(),那么绑定的就是服务端的地址;如果是在客户端中使用bind(),那么绑定的就是客户端的IP地址 )、端口号等进行绑定。
头文件
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:就是套接字的描述符,即socket()的返回值。
const struct sockaddr *addr:一般我们用的结构体是sockaddr_in,所以要进行强制类 型转换,这个参数要写经过强制类型转换的sockaddr_in结构体变量的地址。
addrlen:所要绑定的结构体变量需要占多少个字节,用sizeof()。
返回值:成功返回0,否则返回-1。
设置erron进行检测。
struct sockaddr_in server_addr;
if (-1 == bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(server_addr)))
{
perror("Bind error");
exit(-1);
}
其实,虽然服务器和客户端都可以bind(),但是客户端进行绑定没有意义,因为服务器中的accept()只能接受不能查找。
7,监听函数:listen()
功能:使得一个进程可以接受其他进程的请求,从而成为一个服务器进程。在TCP编程中,是listen()函数把一个进程变成了服务器进程。UDP通信中服务器中没有listen(),所以UDP通信中本质上并没有服务端和客户端,它们都可以主动连接别人,也可以接受别人的连接。也可以说,每台主机即是客户端,又是服务器。
头文件
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockfd:本地创建套接字的描述符。
backlog:一个限制同时连接服务器的客户端数量的数字。
返回值:成功返回0,失败返回-1。
listen()函数的工作流程:listen()用于创建一个等待队列,用来存放未处理的客户端连接。由于TCP是要经过三次握手的可靠性连接,所以需要时间。当同时有多个客户端连接服务器时listen()就会创建两个非循环等待队列,一个是需要连接的客户端队列排序,然后依次进行三次握手。之后进入第二个队列,等待accept()接收取用。而listen()设置的就是队列中客户端的个数。形象的说就是限制传送带只能放十个零件,只有送过去一个,才能添加一个。
设置erron:
int sockfd; //用于存放创建的套接字的描述符
if (-1 == listen(sockfd, 10))
{
perror("Listen error");
exit(-1);
}
8,接收函数:accept()
功能:等待(阻塞)并接收客户端的连接请请求。获取客户端信息。
头文件
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:成功返回连接到的客户端的套接字描述符,失败返回-1。
int sockfd; //用于存放创建的套接字的描述符
if (-1 == accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen))
{
perror("Accept error");
exit(-1);
}
sockfd:表示在等待的套接字,就是服务器的套接字。
addr:客户端地址一般(NULL)。
addrlen:地址长度一般(NULL)。
这里需要注意的是,第二个参数和第三个参数如果都不填,那么可以都写NULL,但是只要一个不是NULL,那另一个也不能写NULL。
设置erron:
int client_fd; //存放连上来的套接口的描述符
struct sockaddr_in client_addr;int sin_size = sizeof(struct sockaddr_in); //存放client_addr所在结构体的大小
if (-1 == (client_fd=accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size)))
{
perror("Accept error");
exit(-1);
}
调用完accept()之后服务器就拥有两个套接字描述符:一个是服务器本身的套接字描述符,用于监听服务器端设置的端口有没有客户端连上来。另一个是accept()返回的客户端的套接字描述符,用于发送和接收数据。
9,连接:connect()
功能:在TCP中是用于bind()之后的client端,用于连接服务器端。在UDP中有点类似与bind()的作用,基本上不需要。
头文件
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
sockfd:客户端的套接字描述符。
serv_addr:客户端想要连接的服务器端的IP地址、端口号、IP地址的类型等信息。
addrlen:地址长度。
10,读函数:read(),recv(),recvfrom()
①read()
该函数的原型为:
# include <unistd.h>
ssize_t read(int fd, void * buf, size_t count);
ssize_t即long型,size_t即unsigned long型。ssize_t的第一个s就是signed,即有符号;
返回值:读取成功返回从该文件读取到buf中的字节数。读到文件末尾或无数家返回0。出现错误返回-1,并设置errno;
②recv()
该函数的原型为:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
recv()的前三个参数就像read()函数一样,它也是默认阻塞的(即直到读取到数据为止),但是如果以recv()的第四个参数flags设置成MSG_DONTWAIT,那么recv()就是非阻塞的了。
像read()和write()函数配套一样,recv()和send()函数配套使用。
返回值:成功返回接收到的字符数,失败返回-1。
③recvfrom()
功能:接受客户端发送的数据信息并获取客户端的IP地址和端口号信息。
介绍:recvfrom()获取的客户端的IP地址和端口号等信息由系统自动分配。recvfrom()配合sendto()函数使用。TCP编程中读取数据可以使用read(),也可以使用recv();但是 UDP编程中只能使用recvfrom() 。recv()是通过client的套接字进行读写数据,而UDP是无连接的,client没有connect(),sever也就没有accept(),即UDP无法通过客户端的套接字读写数据,所以不能使用read()和recv()。UDP不是通过套接字而是通过IP地址和端口号等信息。
该函数的原型为:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_from, socklen_t *fromlen);
sockfd:本地套接字描述符。
flags:是否堵塞的标志。(MSG_DONTWAIT)
from: 源主机的IP地址和端口号信息。
fromlen:地址长度。
返回值:成功返回接收到的字符数,失败返回-1。
值得一提的是:给第五个参数赋值时,我们需要定义一个结构体变量,然后将其地址作为参数。第六个但是就是这结构体变量的长度,所以也需要取地址。
例如:
int sockfd;
char buf[1024];
struct sockaddr_in addr;
int addrlen = sizeof(addr);

addrlen=sizeof(struct sockaddr);
recvfrom(sockfd, buf, 1024, 0, (struct sockaddr *)&addr, &addrlen);
11,写函数:write()、send()、sendto()
与上述配套的读函数参数对应一致,就不详述了。值得注意的是:对于sendto( )函数
其原型为:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
最后两个参数就是 对方 的IP地址和端口等信息,但是和recvfrom()不同, sendto()中最后一个参数不是指针,所以不需要加取地址,直接写sizeof()即可。
12,关闭close()
主要是提示每个程序的文件描述符都需要及时关掉,作为习惯来看。
13,头文件总结

# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <pthread.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>

代码段后面会跟上!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值