1.套接字socket编程:网络通信程序的编写
- 网络通信程序的编写使用的都是套接字接口
- 网络通信程序的编写:TCP/IP五层中应用层是面向程序员的一层,应用层的协议都是程序员自己订立,应用层处理完毕后,将数据交给操作系统,进行传输层开始往下的封装。
- 传输层的典型协议:UDP/TCP
2.UDP协议和TCP协议的区别
- UDP协议:用户数据包协议-----无连接的,不可靠的,面向数据包的一种传输方式(无法提供可靠传输,但是传输速度快)–视频传输
- TCP协议:传输控制协议----基于连接的,可靠的,面向字节流的一种传输方式(提供可靠传输,但是传输速度较慢)–普通的文件传输
3.UDP通信程序的编写
- 在网络通信中,都是端与端之间的通信,两台主机的两个进程间的通信,并且通信两端有一种叫法:
客户端:通信两端中,首先主动发起请求的一端
服务端:通信两端中,首先被动接受请求的一端
4.通信流程
- server(服务端)
- 1.在程序中创建套接字—使进程与网卡之间通过套接字建立联系
- 2.为套接字绑定地址信息
①地址信息:IP地址和端口
②每一条网络中的数据都要包含:源端IP,PORT,对端IP,PORT
③接收端:告诉操作系统发往这个主机的那个端口的数据应该交给我处理
④发送端:告诉操作系统,发送的这条数据的源端地址是什么
⑤就是在创建套接字创建的socket结构体中描述地址信息
- 3.服务端首先接收数据
①创建套接字,实际上在内核中创建了一个结构体–socket,socket中会有两个缓冲区(发送/接收)
②一旦网卡接收到数据,这时候操作系统会根据数据中的目的地址信息,去socket列表中进行匹配,这个数据应该放到谁的接收缓冲区中
③接收数据,只是通过接收接口直接从指定的socket接收缓冲区中取出数据
- 4.发送数据:将数据放到指定socket的发送缓冲区中,操作系统会在何时的时候,从缓冲分区中取出数据进行封装,最后传输出去
- 5.关闭套接字,释放资源
- client(客户端)
- 1.创建套接字,在内核中创建socket结构体
- 2.为套接字绑定地址信息(不推荐)
①刻画段不推荐程序员主动绑定地址,因为一个端口执行被一个进程占用,一旦要绑定的端口已经被使用,则会绑定失败
②尽可能的避免了地址冲突的概率
5.接口介绍
-
① 创建套接字:在内核中创建socket结构体
int socket(int domain, int type, int protocol); -
domain:地址域类型(地址有各种结构—ipv4,ipv6,local)–表示这是一个什么样通信套接字 AF_INET—ipv4协议版本的地址域
-
type:套接字类型(SOCKET_STREAM—流式套接字—默认协议是tcp,SOCKET_DGRAM—数据报套接字—默认是udp)
-
protocol:协议类型—0表示要解字类型的默认协议;IPPROTO_TCP-6 / IPPROT_UDP-17
-
返回值:返回套接字操作句柄—文件描述符;失败返回-1
- ② 为套接字绑定地址信息:在创建的socket结构体中描述源端的地址信息,告诉操作系统那些数据放到自己的接收缓冲区
- int bind(int sockfd, struct sockaddr*addr, socklen_t len)
- sockfd:创建套接字返回的操作句柄
- addr:要绑定的地址信息----struct sockaddr 通用地址结构(通常不用这个)–
- len:地址信息长度
- ipv4使用的是struct sockaddr_in{sin_family—地址域类型;sin_port—网络字节序端口;sin_addr.s_addr–网络字节序IP地址}
6.UDP实现
// udp客户端通信程序
// 1.创建套接字
// 2.绑定地址信息(不推荐)
// 3.接收数据
// 4.发送数据
// 5.关闭套接字
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h> // socket结构头文件
#include <netinet/in.h> // 地址结构头文件
#include <arpa/inet.h> // 网络字节序转换接口头文件
int main(int argc, char* argv[])
{
// argc表示运行参数的个数
// argv存储程序的运行参数,运行参数是运行程序的时候,以空格间隔跟在程序后边
// ./udp_cli 192.169.*.* 9000
if(argc != 3)
{
printf("usage:./udp_cli 192.169.*.* 9000");
return -1;
}
char* ip = argv[1];
uint16_t port = atoi(argv[2]);
// socket(地址域,套接字类型,协议类型)
int sockfd = socket(AF_INET,SOCK_DGRAM, IPPROTO_UDP);
if(sockfd < 0)
{
perror("socket perror");
return -1;
}
// bind(套接字描述符,地址信息,地址信息长度)
struct sockaddr_in addr; // 使用的ipv4的地址结构
addr.sin_family = AF_INET; // 地址域类型
addr.sin_port = htons(port); // 网路字节序端口信息-主要使用htons,而不是htonl
addr.sin_addr.s_addr = inet_addr(ip); // inet_addr将字符串地址转换为网络字节序整型地址
socklen_t len = sizeof(addr);
int ret = bind(sockfd, (struct sockaddr*)&addr, len);
if(ret < 0)
{
perror("bind error");
return -1;
}
return 0;