云计算百科
云计算领域专业知识百科平台

网络编程TCP连接服务器与UDP连接服务器

网络编程TCP连接服务器与UDP连接服务器

网络编程相关知识

局域网和广域网的概念

局域网:

一般是由连在一个路由器下的设备组成的一个网络

广域网

将一个个的局域网连接在一块组成网络

链接一个服务器的流程

1)在浏览器输入要访问的网址https://github.com/

2)浏览器会先去访问 dns 地址解析服务器拿到域名对应的 ip

3)通过 ip 地址找到服务器

4)服务器会回数据给浏览器

5)浏览器会将服务器返回的数据进行整理

ip 地址的分类

IPv4 出现的时间比较久 IPv4 地址的数目 2^32,ipv4 的地址大概在 2015 年左右就分配完了

IPv6 共有 16 字节 128 位 2^128,为地球上的每一粒沙子分配一个 ip 地址

IPv4 地址的分类
A 类地址

第一个字节是固定的后三个字节是可变的

1.0.0.0-127.255.255.255

共有 127 个 A 类地址

一个 A 类地址 可以接 2^24 个设备,一般用于大型或者超大型的公司

B 类地址

前两个字节是固定的 后两个字节是可变的

128.0.0.0-191.255.255.255

一般用于中型的公司,每个 B 类地址可接 2^16 个设备

C 类地址

前三个字节是固定的后一个字节是可变的

192.0.0.0-223.255.255.255

一般用于局域网 也是我们日常生活中比较常见的 ip 地址

D 类地址

224.0.0.0-239.255.255.255

组播广播的地址

E 类地址

240.0.0.0-255.255.255.255 保留地址

端口号

用来对进程做区分的,由于网络编程是跨主机的进程间的通讯,不能用进程号来对进程进行区分

端口号是一个 16 位无符号整型数

范围:0-65535

0-1023 :被系统占用

1024 – 65535:可以使用的

网络字节序

在整个编程界可以接触到的字节序主要有两种

大端模式

在网络通讯里一般使用的是大端模式

高字节存放在低地址位

低字节存放在高地址位

小端模式

在 x86 主机上我们采用的就是小端模式

高字节存在高地址位

低字节存放在低地址位

客户端链接服务端的方式

TCP:

面向链接,可靠的数据传输 传输速度相对较慢

UDP

面向非连接的 不可靠的数据传输 传输速度相对较快

TCP 的三次握手

套接字

套接字的本质就是一个特殊的文件描述符

可以读也可以写

读或者写的时候可以采用 read 和 write

也可以采用 套接字专用的函数 send 和 recv

特殊在套接字可以绑定 ip 端口

TCP 的服务端模型

创建套接字

函数的头文件

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

函数的原型

int socket(int domain, int type, int protocol);

函数的参数

int domain, IP 地址的类型

AF_INET ipv4

AF_INET6 ipv6

int type, 套接字的类型

SOCK_STREAM 流式套接字 TCP

SOCK_DGRAM 数据报 UDP

int protocol 0

函数的返回值

成功返回 套接字

失败返回 -1

绑定 ip 端口信息

函数的头文件

#include <sys/types.h> /* See NOTES

#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; ip 地址的类型
char sa_data[14]; 包含了 ip 地址和端口的字符串
}

struct sockaddr_in
{
sa_family_t sin_family; ip 地址的类型
in_port_t sin_port; 端口号
struct in_addr sin_addr; ip 地址的结构体
};

struct in_addr
{
uint32_t s_addr; ip 地址
};

将小端的端口号转换成大端

函数的功能

将小端的端口转换成大端

函数的头文件

#include <arpa/inet.h>

函数的原型

uint16_t htons(uint16_t hostshort);

函数的参数

uint16_t hostshort:要转换的小端的端口

函数的返回值

成功返回 大端的端口号

将小端的 ip 地址转换成大端的 ip 地址

函数的功能

将小端的 ip 转换成大端的 ip

函数的头文件

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

函数的原型

in_addr_t inet_addr(const char *cp);

函数的参数

const char *cp:小端的 ip 地址

函数的返回值

成功返回 大端的 ip 地址

监听网络

函数的功能

监听网络

函数的头文件

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

函数的原型

int listen(int sockfd, int backlog);

函数的参数

int sockfd, 服务端本身的套接字

int backlog 最大监听数

函数的返回值

成功返回 0

失败返回 -1

接受客户端的链接

函数的头文件

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

函数的原型

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

函数的参数

int sockfd, 服务端本身的套接字

struct sockaddr *addr, 客户端的 ip 地址信息结构体

socklen_t *addrlen 结构体长度的指针

函数的返回值

成功返回 专门用来给客户端通讯的套接字

失败返回 -1

收发消息

函数的头文件

#include <sys/types.h>
#include <sys/socket.h>

函数的原型

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

函数的参数

sockfd : 用来通讯的套接字

buf: 发送/接收消息的存放的位置

len: 要收发的消息的大小

flags: 标志 0 阻塞的收发

函数的返回值

成功返回 成功发送/接收的字节数

失败返回 -1

将大端地址转换小端的字符串形式的 ip 地址

将大端的地址转换成小端

函数的头文件

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

函数的原型

char *inet_ntoa(struct in_addr in);

函数的参数

struct in_addr in: 用来存放大端的 ip 地址的结构体

函数的返回值

成功返回 小端的 ip 地址

失败返回 NULL

实例

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>//服务端

int sfd,cfd,ret;

struct sockaddr_in seraddr;
struct sockaddr_in cliaddr;
socklen_t len=sizeof(cliaddr);
char buf[10]={0};
int main()
{
sfd = socket(AF_INET,SOCK_STREAM,0);//1.创建套接字
if(sfd < 0)
{
perror("socket");
return -1;
}

seraddr.sin_family=AF_INET;//地址类型
//端口号//转换小端到大端
seraddr.sin_port=htons(4444);
//将小端IP转换为大端IP
seraddr.sin_addr.s_addr=inet_addr("192.168.39.201");
//2.绑定 ip 端口信息
ret= bind(sfd, (struct sockaddr *)&seraddr,sizeof(seraddr));
if(ret < 0)
{
perror("bind");
return -1;
}

//3.监听网络
ret = listen(sfd,10);
if(ret < 0)
{
perror("listen");
return -1;
}

//4.接受客户端的连接请求
cfd=accept(sfd,(struct sockaddr *)&cliaddr,&len);
if(cfd < 0)
{
perror("accept");
return -1;
}
//将大端的地址转换为小端模式
printf("客户端IP:%s\\n",inet_ntoa(cliaddr.sin_addr));

// write(cfd,"LQHXX",6);
send(cfd,"LQHXXX",7,0);

// read(cfd,buf,10);
recv(cfd,buf,10,0);
printf("buf=%s\\n",buf);

close(cfd);//关闭套接字
close(sfd);
return 0;
}

TCP 的客户端模型

创建套接字

连接服务器

函数的头文件

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

函数的原型

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

函数的参数

int sockfd, 套接字

const struct sockaddr *addr, 服务器的 ip 地址信息结构体

socklen_t addrlen 结构体的大小

函数的返回值

成功返回 0

失败返回 -1

接收消息

发送消息

关闭套接字

实例

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//客户端

int fd,ret;
struct sockaddr_in seraddr;//服务端地址信息

char buf[10]={0};
int main()
{

fd = socket(AF_INET,SOCK_STREAM,0);//1.创建套接字
if(fd < 0)
{
perror("socket");
return -1;
}
// 连接服务器
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(4444);//服务端端口
seraddr.sin_addr.s_addr = inet_addr("192.168.39.201");
ret = connect(fd,(struct sockaddr *)&seraddr,sizeof(seraddr));
if(ret < 0)
{
perror("connect");
return -1;
}
//读取消息
read(fd,buf,10);
printf("buf=%s\\n",buf);
//写消息
write(fd,"nihao\\n",6);

//关闭套接字
close(fd);
return 0;
}

UDP 的通讯流程

创建套接字

绑定自己的信息

收消息

函数的头文件

#include <sys/types.h>
#include <sys/socket.h>

函数的原型

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 对方的 ip 地址信息的长度

函数的返回值

成功返回成功接收的字节数

失败返回 -1

发消息

函数的头文件

#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);

函数的参数

int sockfd, 套接字

const void *buf, 要发送的消息存放的缓冲区

size_t len, 要发送的消息的长度

int flags, 0 阻塞的发

const struct sockaddr *dest_addr, 要发送的对象的 ip 地址信息结构体

socklen_t addrlen 信息结构体的大小

函数的返回值

成功返回 成功发送的字节数

失败返回 -1

关闭套接字

实例

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int fd,ret;
struct sockaddr_in seraddr,cliaddr;
socklen_t len = sizeof(cliaddr);
char buf[10]={0};
int main()
{

//1创建套接字
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0)
{
perror("socket");
return -1;
}
//绑定IP信息
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(5555);
seraddr.sin_addr.s_addr = inet_addr("192.168.39.201");
ret = bind(fd,(struct sockaddr *)&seraddr,sizeof(seraddr));
if(ret < 0)
{
perror("bind");
return -1;
}
// 收消息
recvfrom(fd,buf,10,0,(struct sockaddr *)&cliaddr, &len);
printf("buf=%s\\n",buf);

// 发信息
sendto(fd,"LQHXX",6, 0,(struct sockaddr *)&cliaddr, sizeof(cliaddr));//作为服务端

close(fd);
return 0;
}

UDP 的组播

组播

一个消息的发送者发送消息

加入到多播组的所有用户都能听到

本来 udp 通讯就支持组播

默认 udp 的组播他的属性是被关闭掉了

要想能够使用组播

就需要先将这个属性打开

要想打开这个属性就必须借助于一个函数

setsockopt()

组播还需要一个特殊的 ip 地址

224.0.0.0-239.255.255.255

设置套接字的属性

函数的头文件

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

函数的原型

int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);

函数的参数

int sockfd, 要设置属性的套接字

int level, 网络的层

IPPROTO_IP

SOL_SOCKET

int optname, 要设置的属性的名字

IP_ADD_MEMBERSHIP 加入多播组

IP_MULTICAST_IF 创建多播组

SO_BROADCAST 打开广播

SO_REUSEADDR ip 地址复用

const void *optval, 属性相关的参数

socklen_t optlen 参数的大小

函数的返回值

成功返回 0

失败返回 -1

实例

创建多播组

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//创建多播组
int sfd,ret;
struct ip_mreqn mygrop;
struct sockaddr_in addr;
int main()

{
sfd = socket(AF_INET,SOCK_DGRAM,0);//1.创建套接字
if(sfd < 0)
{
perror("socket");
return -1;
}

mygrop.imr_multiaddr.s_addr = inet_addr("224.224.224.224");//组播地址
mygrop.imr_address.s_addr = inet_addr("192.168.39.201"); //服务器地址
mygrop.imr_ifindex= if_nametoindex("ens33");//网卡

ret = setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_IF,
&mygrop,sizeof(mygrop));

if(ret< 0)
{
perror("setsockopt");
return -1;
}

while(1)
{
addr.sin_family = AF_INET;//IPV4
addr.sin_port = htons(4444);
addr.sin_addr.s_addr = inet_addr("224.224.224.224");//组播地址
sendto(sfd,"LQHXX",6,0,(struct sockaddr *)&addr,sizeof(addr));
sleep(1);
}
return 0;

}

加入一个多播组

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//加多播组
int sfd,ret;
struct ip_mreqn mygrop;
struct sockaddr_in cliaddr,addr;
socklen_t len = sizeof(struct sockaddr_in);
int main()

{
sfd = socket(AF_INET,SOCK_DGRAM,0);//1.创建套接字
if(sfd < 0)
{
perror("socket");
return -1;
}

//绑定自己的IP信息
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(4444);
cliaddr.sin_addr.s_addr = inet_addr("0.0.0.0");
ret = bind(sfd ,(struct sockaddr *)&cliaddr,sizeof(cliaddr));

//加入组播
mygrop.imr_multiaddr.s_addr = inet_addr("224.224.224.224");//组播地址
mygrop.imr_address.s_addr = inet_addr("192.168.39.201"); //服务器地址
mygrop.imr_ifindex= if_nametoindex("ens33");//网卡
ret = setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mygrop,sizeof(mygrop));

if(ret< 0)
{
perror("setsockopt");
return -1;
}

while(1)
{
char buf[10]={0};
recvfrom(sfd,buf,10,0,(struct sockaddr *)&addr,&len);
printf("%s:%s\\n",inet_ntoa(addr.sin_addr),buf);
}
return 0;

}

现象

赞(0)
未经允许不得转载:网硕互联帮助中心 » 网络编程TCP连接服务器与UDP连接服务器
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!