linux网络编程笔记--tcp/udp

网络编程

网络编程所使用的套接字也属于进程之间的通信方式,但区别在于它可以跨主机通信,利用网络通信协议
网络通信基础:
1、网络体系结构模型:
OSI模型(7层)
应用层:老板说话
表示层:秘书写信
会话层:前台封装
传输层:选择通信协议:TCP协议/UDP协议
网络层:解析IP地址
数据链路层:选择路线
物理层:网线/网卡
OSI模型在发送的过程中:数据从应用层出发,每经过一层就会在数据后面加上这一层的"头",除了最后的物理层
OSI模型在接收的过程中:数据从物理层传入,每经过一层就会去掉一层的"头",最后到的应用层就只剩数据了

TCP/IP模型(4层)
应用层:
传输层:
网络层:
网络接口与物理层:

2、什么是IP地址?
例如:“192.168.13.194”
用来查找通信对象网络位置的地址
在同一个局域网下,每一台主机都必须要有一个IP地址,且IP地址是相同网段:IP地址的前三个数字
所以只能通过最后一个数字来划分给不同的主机使用
每一个IP地址都是32位,如果要在网络编程中使用IP地址,要按照网络字节序保存

3、什么是端口号?
在同一台主机下每一个进程都共用一个IP地址,如何区分同一主机下不同的进程?
通过端口号,每一个进程都会被分配一个端口号
端口号是16位,取值范围:0~65535
系统默认占用端口:0~1023
端口号在结束使用后会被回收,所以最好过一段时间在使用

4、ubuntu网络配置:
要让ubuntu和电脑主机处于同一网段下
先改主机IP地址:
打开电脑设置,点击网络,选择以太网,点击"更改适配器选项"
选择以太网,右击选择"属性",双击"Internet 协议版本4 (TCP/IPv4)"
IP地址:192.168.13.xxx
子网掩码:点击一下自动生成
网关:192.168.13.1
首选DNS:202.103.24.68
最后点击确定就行
再修改ubuntuIP地址:
打开虚拟机设置,修改网络适配器,选择桥接模式或nat模式
输入指令查看网卡名称:ifconfig
gec@ubuntu:/mnt/hgfs/wh2302/6-网络编程/day1$ ifconfig
ens33(网卡) Link encap:Ethernet 以太网
HWaddr 00:0c:29:fe:89:1e 硬件地址
inet addr:192.168.13.168 IP地址
Bcast:192.168.13.255 广播地址
Mask:255.255.255.0 子网掩码
配置IP地址:
sudo ifconfig 网卡名称 IP地址
例如:sudo ifconfig ens33 192.168.13.4
检查ubuntu和主机之间能否通信
ping 其他主机IP地址
gec@ubuntu:/mnt/hgfs/wh2302/6-网络编程/day1$ ping 192.168.13.3
PING 192.168.13.3 (192.168.13.3) 56(84) bytes of data.
64 bytes from 192.168.13.3: icmp_seq=1 ttl=128 time=0.328 ms
64 bytes from 192.168.13.3: icmp_seq=2 ttl=128 time=0.121 ms
如果出现这种信息,说明能通信
在主机也验证一下:
打开主机终端:win+R打开运行框,输入cmd回车
在终端输入ping指令,例如: ping 192.168.13.4
正在 Ping 192.168.13.4 具有 32 字节的数据:
来自 192.168.13.4 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.13.4 的回复: 字节=32 时间<1ms TTL=64
如果ping不通
关闭主机防火墙:打开控制面板,控制面板->系统和安全->Windows Defender 防火墙\自定义设置
接着重启虚拟机和电脑,重新配置一遍

5、通信协议:
在不同的主机下,双方要遵循同一规则下的协议来进行通信
TCP/IP协议 传输控制协议/以太网互联协议
TCP协议 用于检查传输过程中出现的问题
IP协议 负责不同网络之间的数据交换
传输的过程中,一旦发生错误会要求重写传输数据,直到数据安全为止
TCP协议和UDP协议的区别:
TCP协议:(打电话)
特点:字节传输,面向连接,效率相对较低
优点:提供可靠传输,确保数据无丢失
缺点:点对点,一次只能和一个对象进行通信
应用场合:重要信息的传输,长时间的传输

UDP协议:(写信)
	特点:数据包传输,面向无连接,效率相对较高
	优点:点对多通信,一次可以和多个对象通信
	缺点:传输时可能会发送数据丢失
	应用场合:网络媒体的直播、视频

=============================================
UDP通信
linux下的网络通信使用的是套接字,那么根据使用通信协议不同,创建的套接字也不同
UDP:数据包式传输->数据包式套接字
通信流程:
发送端:
申请套接字
准备好对方的IP地址和端口号
准备好要发送的数据,发送
关闭套接字
接收端:
申请套接字
绑定IP地址和端口号
等待接收消息
关闭套接字

1、创建套接字:
头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数原型:
int socket(int domain, int type, int protocol);
参数:
int domain:域
AF_INET 网际协议(适用于IPv4地址)
AF_UNIX 本地协议
int type:类型
SOCK_STREAM 数据流(TCP协议)
SOCK_DGRAM 数据包(UDP协议)
int protocol:协议
一般填0就行,因为具体的协议已经根据域和类型决定好了
返回值:
成功:套接字的文件描述符
失败:-1

2、绑定IP地址和端口号
头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
int sockfd:套接字
const struct sockaddr *addr:存放要绑定的IP地址和端口号的结构体的地址
socklen_t addrlen:结构体大小
返回值:
成功:0
失败:-1

结构体原型:
struct sockaddr {
sa_family_t sa_family; //域
char sa_data[14];//存放IP地址和端口号的数组
}
问题:
这个结构体中把IP地址和端口号放在一个char型数组中
而IP地址和端口分别是4字节和2字节的网络字节序存储数据
所以后来为了解决这个问题,就诞生了另一种结构体类型
在去man手册第7本查看ip获得结构体原型:
struct sockaddr_in {
sa_family_t sin_family; /* 域: AF_INET /
in_port_t sin_port; /
端口号 /
struct in_addr sin_addr; /
IP地址 /
};
/
存放IP地址的结构体类型 /
struct in_addr {
uint32_t s_addr; /
网络字节序的IP地址 */
};
所以要保存IP地址和端口号就要先定义这个类型的结构体
将转换成网络字节序后的IP地址端口号保存到结构体中
传参时将该结构体类型强转为sockaddr类型

3、主机字节序转网络字节序
为了方便后续测试代码和修改,我们最好将IP地址和端口号通过外部传参传入程序中
这样一来传入的IP地址和端口号就是字符串了,所以要转化还得先将字符串转化成主机字节序才行
头文件:
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
函数:
将字符串转化为主机字节序的函数:
int atoi(const char *nptr);//传入字符串,返回一个主机字节序数据
主机字节序和网络字节序转化函数:
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h:host(主机)
n:net(网络)
l:32位无符号整型数据
s:16位无符号整型数据
但IP地址是按点分制传入的,要转化为主机字节序会有些问题,所以有专门的函数:
int inet_pton(int af, const char *src, void *dst);
参数:
int af:要转化的地址类型(AF_INET:IPv4 / AF_INET6:IPv6)
const char *src:要转化的IP地址字符串,例如:“192.168.13.3”
void *dst:保存转化后的IP地址的结构体的地址
如果要将网络字节序的IP地址转化成字符串?
char *inet_ntoa(struct in_addr in);
传入网络字节序的IP地址,返回一个点分制的字符串

4、发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
int sockfd:套接字
const void *buf:要发送的数据
size_t len:要发送的字节数
int flags:发送标志:默认填0
const struct sockaddr *dest_addr:保存对方IP地址和端口号的结构体地址
socklen_t addrlen:结构体大小
返回值:
成功:发送的字节数
失败:-1

5、接收数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
参数:
int sockfd:套接字
void *buf:存放接收到的数据的内存
size_t len:要接收的字节数
int flags:接收标志:默认填0
struct sockaddr *src_addr:保存发送者的IP地址和端口号的结构体
socklen_t *addrlen:结构体大小,但是地址传递
返回值:
成功:接收的字节数
失败:-1

6、关闭套接字
close

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值