IP地址有IPV4、IPV6之分,一般不特俗说明,默认就是IPV4。
IP地址是用来标识不同的主机,每个主机都有唯一的IP地址;
对于IP4来说,IP地址是一个4字节,32位整数;
IP地址用“点分”制表示,如:192.168.1.11(用点分割的每个数范围0~255)。
很容易理解,都是地址,和寄快递的收发地址一样,从上海发往西安的快递,源IP就是上海,目的IP就是西安。
端口号是2字节196位整数;
端口号用来标识一个进程,告诉操作系统,当前数据要交给哪一个进程来处理;
一个进程可以绑定多个端口号,但是一个端口号不可以绑定多个进程。
在源IP&目的IP中,我们用的寄快递的例子帮助理解,在这里,还是用发快递帮助理解。源IP与目的IP标识了发件人地址和收件人的地址,地址有了,那么包裹就会交给快递员运送每个,快递员都有一个工号,工号是唯一的。这就对应了数据传输过程中,由哪个进程来处理数据。再来到寄快递问题上,有的快递包裹比较大,这就要多个快递员来运输,那么一个包裹由多个快递员运输,记在物流信息上就是这样的格式:一个包裹的目的地 + 多个快递员工号;这家公司接的都是大包裹,一个快递员只能送一个包裹。对应到网络传输中,就是一个进程可以绑定多个端口号,但一个端口号不可以绑定多个进程。
在C语言中我们知道,内存中的数据存储有大小端之分;数据在磁盘中存储也有大小端之分,在这里我还想啰嗦一个C语言问题,怎样判断自己的计算机内存是大端字节序还是小端字节序存储方式?[假装思索……]
附上链接:
(只想引入下面一句话)在网络数据流中同样有大小端之分,那么如何定义网络数据流的地址呢?
【看图理解】
【看图说话】
为了使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
调用函数就能解决存储字节序不统一的问题
这部分只把函数列出来,详细介绍请戳作者下面博客:
socket套接字
//创建socket文件描述符 (TCP/UDP,客户端+服务器)
int socket(int domain, int type, int protocol);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
//关闭套接字
int close(int fd);
socket API是一 层抽象的网络编程接口 ,适用于各种底层网络协议,如IPv4、IPv6.然而, 各种网络协议的地址格式各不相同。
- 通过上面的学习对UDP有个直观的认识,再详细讨论以下几点:
知道目的端的IP和端口号就能传输,不需要建立连接。
没有确认机制,没有重传机制,如果因为网络故障无法发送到对方,UDP协议层也不会给应用层返回任何错误信息。
不能够灵活的控制读写数据的次数和数量。
1、创建socket
2、绑定端口
3、循环的读取数据
4、针对读取到的数据进行计算和处理
5、把处理后的结果发回客户端
1、创建socket文件
2、给服务器发送请求
3、从服务器中读取结果。
#include
#include
#include
#include
#include
#include
int main()
{
//创建一个套接字,并检测是否创建成功
int sockSer = socket(AF_INET, SOCK_DGRAM, 0);
if(sockSer == -1)
perror("socket");
struct sockaddr_in addrSer; //创建一个记录地址信息的结构体
addrSer.sin_family = AF_INET; //使用AF_INET协议族
addrSer.sin_port = htons(5050); //设置地址结构体中的端口号
addrSer.sin_addr.s_addr = inet_addr("192.168.3.169"); //设置通信ip
//将套接字地址与所创建的套接字号联系起来,并检测是否绑定成功
socklen_t addrlen = sizeof(struct sockaddr);
int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen);
if(res == -1)
perror("bind");
char sendbuf[256]; //申请一个发送数据缓存区
char recvbuf[256]; //申请一个接收数据缓存区
struct sockaddr_in addrCli;
while(1) //服务器一直循环接受客户端的请求
{
recvfrom(sockSer,recvbuf,256,0,(struct sockaddr*)&addrCli, &addrlen); //从指定地址接收客户端数据
printf("Cli:>%s\n",recvbuf);
printf("Ser:>");
scanf("%s",sendbuf);
sendto(sockSer,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&addrCli, addrlen); //向客户端发送数据
}
return 0;
}
#include
#include
#include
#include
#include
#include
int main()
{
//创建一个套接字,并检测是否创建成功
int sockCli = socket(AF_INET, SOCK_DGRAM, 0);
if(sockCli == -1){
perror("socket");
}
addrSer.sin_family = AF_INET; //使用AF_INET协议族
addrSer.sin_port = htons(5050); //设置地址结构体中的端口号
addrSer.sin_addr.s_addr = inet_addr("192.168.3.169"); //设置通信ip
socklen_t addrlen = sizeof(struct sockaddr);
char sendbuf[256]; //申请一个发送数据缓存区
char recvbuf[256]; //申请一个接收数据缓存区
while(1){
//向客户端发送数据
printf("Cli:>");
scanf("%s",sendbuf);
sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, addrlen);
接收来自客户端的数据
recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &addrlen);
printf("Ser:>%s\n", recvbuf);
}
return 0;
}