百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Linux网络编程——详解SOCKET linux 网络编程

nanshan 2024-12-23 12:31 16 浏览 0 评论

一、预备知识

大端模式、小端模式

  • 大端字节序(Big Endian):最高有效位存于最低内存地址处,最低有效位存于最高内存处;
  • 小端字节序(Little Endian):最高有效位存于最高内存地址,最低有效位存于最低内存处。

网络字节序

  • 我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分,也就是说,当接收端收到第一个字节的时候,它将这个字节作为高位字节还是低位字节处理
  • 网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址
  • TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,因此网络数据流应采用大端字节序,即低地址高字节

可以调用以下库函数做网络字节序和主机字节序的转换

#include <arpa/inet.h>

//这些函数调用成功后返回处理后的值,调用失败则返回-1
uint32_t htonl(uint32_t hostlong);	//主机字节顺序转换为网络字节顺序 对无符号长型进行操作4bytes
uint16_t htons(uint16_t hostshort); //主机字节顺序转换为网络字节顺序 对无符号短型进行操作2bytes  

uint32_t ntohl(uint32_t netlong);   //网络字节顺序转换为主机字节顺序 对无符号长型进行操作4bytes
uint16_t ntohs(uint16_t netshort);  //网络字节顺序转换为主机字节顺序 对无符号短型进行操作2bytes

IP地址转换函数

  • Linux提供了用于将点分十进制表示的IP地址与二进制表示的IP地址相互转换的函数族

早期

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

//数字加点类型 转换成 将32位的IP    IP地址存放在参数straddr中,返回结果存放在addrptr中 
int inet_aton(const char *straddr, struct in_addr *addrptr);

//将32位的IP 转换成 数字加点类型
char *inet_ntoa(struct in_addr straddr);

//数字加点类型 转换成 将32位的IP
in_addr_t inet_addr(const char *cp);

/*只能处理IPv4的ip地址
不可重入函数
注意参数是struct in_addr*/

现在

#include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

/*支持IPv4和IPv6
可重入函数*/

Linuxc/c++服务器开发高阶视频,电子书学习资料后台私信【架构】获取

sockaddr数据结构

  • Linux中定义了一种通用的套接字结构类型strcut sockaddr,以供不同的协议调用
  • strcut sockaddr 很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结
    构体,为了向前兼容,现在sockaddr退化成了(void *)的作用,传递一个地址给函数,至于这个地址类型是sockaddr_in还是sockaddr_in6(文章没有列举出),由地址族确定,然后函数内部再强制类型转化为所需的地址类型
#include <sys/socket.h>

struct sockaddr
 {
	unsigned short sa_family; /* address族, AF_xxx */
	char sa_data[14]; 	      /* 14 bytes的协议地址 */
};

参数sa_family可选择如下

  • AF_INET IPv4协议
  • AF_INET6 IPv6协议
  • AF_LOCAL UNIX协议
  • AF_LINK 链路地址协议
  • AF_KEY 密钥套接字

除了sockaddr以外,Linux中还定义了另外一种结构类型sockaddr_in,它和sockaddr等效且可以互相转换(需要显式转换),通常在涉及TCP/IP的编程协议中使用

#include <netinet/in.h>

struct sockaddr_in
 {
	short int sin_family; 			/* Internet地址族 */
	unsigned short int sin_port;    /* 端口号 */
	struct in_addr sin_addr; 		/* Internet地址 */
	unsigned char sin_zero[8]; 		/* 添0(和struct sockaddr一样大小)*/
};

//其中in_addr由于历史设计原因导致结构体多余
struct in_addr
{
__be32 s_addr;//32位IPv4地址,网络字节序
};

网络设计模式

c/s 客户端/服务器

  • 需要开发客户端服务器,采用自定义协议
  • 必须先下载客户端,数据提前缓冲好
  • 需要考虑安全问题
  • 开发工作量大

b/s web/服务器

  • 不需要安装软件,点击浏览器就可以看到
  • 工作量小,客户端基本浏览器方式
  • 缺点:必须遵循http协议,动态加载数据

二、SOCKET

概述

  • linux中的网络编程通过socket接口实现。socket既是一种特殊的IO,它也是一种文件描述符

socket可以简单理解成为一个插座和插排,那么如何匹配?


就是通过IP+端口号进行匹配,匹配之后可以通过socket进行数据的发送和接收(socket本质是文件描述符fd)


具体的流程如下

socket创建

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

int socket(int domain, int type, int protocol);
  • domain:
    AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
    AF_INET6 与上面类似,不过是采用IPv6的地址
    AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
  • type:
    (1)SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
    (2)SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
    (3)SOCK_SEQPACKET 这个协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的
    接受才能进行读取。
    (4)SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使
    用该协议)
    (5)SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数
    据包的顺序
  • protocol:
    0 默认协议
  • 返回值
    成功返回一个新的文件描述符(也叫监听套接字),失败返回-1

bind绑定

  • 在创建了套接字之后需要IP和端口号和套接字绑定在一起( IP地址:在网络环境中,唯一标识一台主机,端口号:在主机中唯一标识一个进程)
  • 前面讲过,struct sockaddr *是一个通用指针类型,addr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度
#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd
    socket文件描述符
  • addr:
    构造出IP地址加端口号
  • addrlen:
    sizeof(addr)长度
  • 返回值
    成功返回0,失败返回-1, 设置errno

例如

struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));//清0结构体
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(8000);

listen

  • 创建了套接字之后通常需要等待客户端的连接,此时可以使用listen函数将该套接字转换为倾听套接字。
  • 可以指定同时连接的最大客户端数量
  • 若达到数量上限,新客户端等待其它已链接的客户端链接结束
#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);
  • sockfd:
    socket文件描述符
  • backlog:
    排队建立3次握手队列和刚刚建立3次握手队列的连接数和
  • 返回值
    成功返回0,失败返回-1

accept

  • 当服务器倾听到一个连接之后,可以使用函数accept从倾听套接字的完成连接队列中接收一个连接,如果这个完成连接队列为空,则会使得这个进程进入睡眠状态
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockdf:
    socket文件描述符
  • addr:
    传出参数,返回链接客户端地址信息,含IP地址和端口号
  • addrlen:
    传入传出参数,传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小
  • 返回值
    成功返回一个新的socket文件描述符,用于和客户端通信,失败返回-1,设置errno

connect客户端连接函数

  • 客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址
#include <sys/types.h> 
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockdf:
    socket文件描述符
  • addr:
    传入参数,指定服务器端地址信息,含IP地址和端口号
  • addrlen:
    传入参数,传入sizeof(addr)大小
  • 返回值
    成功返回0,失败返回-1,设置errno

读写函数

<unistd.h>

int read(int fd, char *buf, int len);
int write(int fd, char *buf, int len);
  • fd
    套接字描述符;
  • buf
    指定数据缓冲区;
  • len
    指定接收或发送的数据量大小(以字节为单位)。
  • 返回值
    返回读/写成功的数据量大小,失败则返回-1。

关闭函数

<unistd.h>

int close(int fd);
  • fd
    套接字描述符;

写一个服务器例子和客户端例子

服务器

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

#define Port 6666 //端口号
#define MAXCLIENT 10 //最大客户端数量

int main(int argc, char argv[])
{
	int socket_fd, client_fd;
	int ret;
	int addr_size;
	struct sockaddr_in server_addr;   
	struct sockaddr_in client_addr; 
	
	int read_size;
	char buffer[1024]; 
	
	//创建socket
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if( socket_fd == -1)
	{
		printf("socket error\n");
		exit(1);
	}
	
	//绑定bind
	bzero(&server_addr, sizeof(struct sockaddr_in));//清空数据
	
	server_addr.sin_family = AF_INET;//IPv4
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//将主机IP转换为网络IP
	server_addr.sin_port = htons(Port);//将主机端口转换为网络Port	
	
	ret = bind(socket_fd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr));
	if(ret == -1)
	{
		printf("bind error\n");
		exit(1);
	}
	
	//监听
	ret = listen(socket_fd, MAXCLIENT);
	if(ret == -1)
	{
		printf("listen error\n");
		exit(1);
	}
	
	while(1)
	{
		//accept
		addr_size = sizeof(struct sockaddr_in);
		client_fd = accept(socket_fd, (struct sockaddr *)(&client_addr), &addr_size);
		if(client_fd == -1)
		{
			printf("accept error\n");
			exit(1);
		}
		//打印客户端IP   将网络地址转换成 .字符串 
		printf("Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
			
		if((read_size = read(client_fd, buffer, 1024)) == -1)    
		{     
			printf("Read Error\n");     
			exit(1);    
		} 
  	     
		buffer[read_size]='\0';   
		printf("Server received %s\n",buffer); 
			
		close(client_fd);    /* 循环下一个 */   		
	}
	
	close(socket_fd);   	
	return 0;
}

客户端

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

#define Port 6666

int main(int argc, char argv[])
{
	int socket_fd;
	int ret;
	char buff[1024];
	struct sockaddr_in server_addr;
	
	char* str_IP = "172.21.252.7";
	
	//创建客户端socket
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if( socket_fd == -1)
	{
		printf("socket error\n");
		exit(1);
	}
	
	//连接connect
	bzero(&server_addr, sizeof(struct sockaddr_in));//清空数据
	
	server_addr.sin_family = AF_INET;//IPv4
	server_addr.sin_addr.s_addr = inet_addr(str_IP);//将主机IP转换为网络IP
	server_addr.sin_port = htons(Port);//将主机端口转换为网络Port

	ret = connect(socket_fd, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr_in));
	if(ret == -1)
	{
		printf("connect error\n");
		exit(1);
	}
	
	while(1)
	{
		//连接成功了,发送数据
		printf("Please input char:\n");     
		fgets(buff, 1024, stdin);   
		write(socket_fd, buff, strlen(buff)); 
	}
	
	close(socket_fd);
	return 0;
}

运行结果如下

注意

可通过nc指令测试服务器是否有误

相关推荐

Let’s Encrypt免费搭建HTTPS网站

HTTPS(全称:HyperTextTransferProtocoloverSecureSocketLayer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入...

使用Nginx配置TCP负载均衡(nginx tcp负载)

假设Kubernetes集群已经配置好,我们将基于CentOS为Nginx创建一个虚拟机。以下是实验种设置的详细信息:Nginx(CenOS8Minimal)-192.168.1.50Kube...

Nginx负载均衡及支持HTTPS与申请免费SSL证书

背景有两台minio文件服务器已做好集群配置,一台是192.168.56.41:9000;另一台是192.168.56.42:9000。应用程序通过Nginx负载均衡调用这两台minio服务,减轻单点...

HTTPS配置实战(https配置文件)

原因现在网站使用HTTPS是规范操作之一,前些日子买了腾讯云服务,同时申请了域名http://www.asap2me.top/,目前该域名只支持HTTP,想升级为HTTPS。关于HTTPS的链接过程大...

只有IP地址没有域名实现HTTPS访问方法

一般来说,要实现HTTPS,得有个注册好的域名才行。但有时候呢,咱只有服务器的IP地址,没注册域名,这种特殊情况下,也能照样实现HTTPS安全访问,按下面这些步骤来就行:第一步,先确认公网...

超详解:HTTPS及配置Django+HTTPS开发环境

众所周知HTTP协议是以TCP协议为基石诞生的一个用于传输Web内容的一个网络协议,在“网络分层模型”中属于“应用层协议”的一种。在这里我们并不研究该协议标准本身,而是从安全角度去探究使用该协议传输数...

Godaddy购买SSL之后Nginx配置流程以及各种错误的解决

完整流程:参考地址:https://sg.godaddy.com/zh/help/nginx-generate-csrs-certificate-signing-requests-3601生成NGI...

Nginx从安装到高可用,一篇搞定(nginx安装与配置详解)

一、Nginx安装1、去官网http://nginx.org/下载对应的nginx包,推荐使用稳定版本2、上传nginx到linux系统3、安装依赖环境(1)安装gcc环境yuminstallgc...

阿里云免费证书申请,配置安装,使用tomcat,支持http/https访问

参数说明商品类型默认已选择云盾证书服务(无需修改)。云盾证书服务类型SSL证书服务的类型。默认已选择云盾SSL证书(无需修改),表示付费版SSL证书。如果您需要免费领取或付费扩容DV单域名证书【免费试...

你试过两步实现Nginx的规范配置吗?极速生成Nginx配置小工具

NGINX是一款轻量级的Web服务器,最强大的功能之一是能够有效地提供HTML和媒体文件等静态内容。NGINX使用异步事件驱动模型,在负载下提供可预测的性能。是当下最受欢迎的高性能的Web...

从零开始搭建HTTPS服务(搭建https网站)

搭建HTTPS服务的最初目的是为了开发微信小程序,因为wx.request只允许发起HTTPS请求,并且还必须和指定的域名进行网络通信。要从零开始搭建一个HTTPS的服务需要下面4...

群晖NAS使用官网域名和自己的域名配置SSL实现HTTPS访问

安全第一步,群晖NAS使用官网域名和自己的域名配置SSL实现HTTPS访问【新手导向】NAS本质还是一个可以随时随地访问的个人数据存储中心,我们在外网访问的时候,特别是在公网IP下,其实会面临着很多安...

让网站快速升级HTTPS协议提高安全性

为什么用HTTPS网络安全越来越受到重视,很多互联网服务网站,都已经升级改造为https协议。https协议下数据包是ssl/tcl加密的,而http包是明文传输。如果请求一旦被拦截,数据就会泄露产生...

用Https方式访问Harbor-1.9版本(https访问流程)

我上周在头条号写过一篇原创文章《Docker-Harbor&Docker-kitematic史上最详细双系统配置手册》,这篇算是它的姊妹篇吧。这篇文章也将用到我在头条写的另一篇原创文章的...

如何启用 HTTPS 并配置免费的 SSL 证书

在Linux服务器上启用HTTPS并配置免费的SSL证书(以Let'sEncrypt为例)可以通过以下步骤完成:---###**一、准备工作**1.**确保域名已解析**...

取消回复欢迎 发表评论: