C++网络编程——socket技术基础

本文介绍了C++中的socket基础知识,包括socket的类型(如流格式套接字、数据报格式套接字、原始套接字)以及C/S架构下的socket设计。详细讲解了常用数据结构如socket描述符、sockaddr、sockaddr_in和in_addr,以及网络与主机字节序的转换。还涵盖了socket相关函数的用法,如socket(), bind(), connect(), listen(), accept(), send()/recv()等。" 52911270,5035029,线性筛法优化:高效计算约数个数,"['算法', '数学', '质数', '筛法', '优化']
摘要由CSDN通过智能技术生成

研究生阶段项目开发用到了socket技术,写个博客简单记录一下socket通信相关的基础知识,包含我个人对socket技术的一些理解,个人经验,如有错误烦请大佬们批评指正

什么是socket

  中文翻译过来叫“套接字”,可以理解为一个通信端点,我们都知道主机与主机之间通信是通过ip和端口(传输层和网络层),那么两台主机上的应用程序(应用层)如果想相互交流,也需要借助主机间的通信机制,但是应用程序不是主机本身,想要使用这一机制,就需要借助于socket,也就是说,socket是连接应用层和各种网络协议的接口。
  socket的通信机制有点类似于我们用手机通信,当两个人需要远程对话的时候,首先要各有一部手机,然后需要插入电话卡(类似于ip地址和端口号),然后一方拨打另一方的手机号码,接通之后,可以相互发送/接收信息。

socket的类型

流格式套接字

  流格式套接字(Stream Sockets)也叫面向连接的套接字,代码中使用SOCK_STREAM表示。提到面向连接,一定是基于TCP协议的,SOCK_STREAM是一种可靠的、双向的通信数据流,在底层实现了重传机制,使得数据可以准确无误地到达另一台计算机。
  SOCK_STREAM的特征:

  • 数据按照顺序传输(早发送的早到)
  • 在传输过程中数据不会丢失(TCP协议保证)
  • 数据发送和接收是不同步的(传输的数据存入缓冲区)
    实际应用:HTTP协议传输数据

数据报格式套接字

  数据报格式套接字(Datagram Sockets),也叫无连接的套接字(听名字就知道基于UDP),代码中使用SOCK_DGRAM表示。拥有UDP协议传输的优缺点,数据传送速度快,消耗小,但是数据容易丢失或损坏
  SOCK_DGRAM的特征:

  • 快速传输(不注重顺序)
  • 数据可能会丢失或损坏(只是小概率事件,只是相对TCP来说不稳定)
  • 数据的发送和接收是同步的
  • 每次传输的数据大小有限制
    实际应用: QQ视频、语音

原始套接字

  原始套接字(Raw Socket)是一个特殊的额套接字类型,代码中使用SOCK_RAW表示,原始套接字的特殊之处在于:能够实现从应用层到数据链路层的所有数据操作。TCP/UDP类型的套接字只能访问传输层以及传输层以上的数据(因为TCP/UDP是传输层协议,当传到传输层的时候,下层的IP包头/帧头帧尾都已经被丢弃)
  关于原始套接字,在此仅仅作一个简单介绍,我了解和使用的不是很多,不敢妄言,感兴趣的同学可以自行百度。

C/S架构下的socket设计

  我参与开发的项目主要是C/S架构(网上的大多数socket教程也都采用C/S架构举例),就从这一方向简单介绍一下。
  下图是我自己绘制的Stream Sockets在客户端和服务器端上的建立创建、连接、通信和关闭流程图,并包含了通过程中各种状态的变化。在这里插入图片描述
  Datagram Sockets的流程与Stream Sockets流程类似,只是其中的TCP协议换成了UDP协议,少了几次握手和挥手的过程。

socket常用数据结构

socket描述符

int类型,用于保存创建好的套接字

sockaddr

struct sockaddr {
   
  unsigned short sa_family; /* 地址家族, AF_xxx */
  char sa_data[14]; /*14字节协议地址*/
};

其中sa_family是两字节的地址家族,一般都是“AF_xxx”的形式,用于指定函数返回的地址信息类型
  AF_INET:返回IPV4地址信息
  AF_INET6:返回IPV6地址信息
  AF_UNSPEC:可以返回任何协议族的地址
我们使用的基本都是第一种。
sa_data包含套接字中的目标地址和端口信息

sockaddr_in

struct sockaddr_in {
   
  short int sin_family; /* 通信类型 */
  unsigned short int sin_port; /* 端口 */
  struct in_addr sin_addr; /* Internet 地址 */
  unsigned char sin_zero[8]; /* 与sockaddr结构的长度相同*/
};

sin_family:等同于sa_family,实际应用中大多选择AF_INET
sin_port:存储端口号
sin_addr:存储IP地址,使用in_addr这个数据就够,并且要使用网络字节序
sin_zero:是为了让sockaddr与sockaddr_in两个结构体保持大小相同而保留的空字节
设计sin_zero,目的就是能让这两个结构体大小相等,进而能够相互转换,实际编程中多数使用第二个结构体来设置和获取地址信息,而作为参数传递时通常转换成sockaddr结构

in_addr

typedef struct in_addr {
   
  union {
   
  struct{
    unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b;
  struct{
    unsigned short s_w1, s_w2;} S_un_w;
  unsigned long S_addr;
  } S_un;
  } IN_ADDR;

是一个存储ip地址的共用体,有三种表达方式
第一种用四个字节表示IP地址的四个数字。
第二种用两个双字节表示IP地址
第三种用一个长整型来表示IP地址
给in_addr赋值最简单的方式就是inet_addr()函数,可以将一个代表IP地址的字符串转换为in_addr类型,其反函数是inet_ntoa()。

sockaddr_in ina;
ina.sin_addr.s_addr=inet_addr("192.168.0.1");

实际使用时需要对inet_addr()的返回值进行检查,如果为-1则说明函数错误,如果不检查无符号的-1则与广播地址255.255.255.255相同

网络与主机字节序的相互转换

需要先介绍一下网络字节序和主机字节序

网络字节序

是TCP/IP中已经规定好的一种数据表示格式,是一种固定格式,保证数据在不同主机之间传输时能够被正确解释。网络字节序采用大端存储方式(低位字节放在内存高位地址,高位字节放在内存低位地址)

主机字节序

主机字节序是多样性的,其存储方式取决于CPU等
判断主机字节序的方法

bool am_little_endian()
{
   
	unsigned short i=1;
	return (int)*((char*)(&i))?true:false;	//返回true则为小端存储
}

转换方法

htons()——主机字节序转换为网络字节序(short类型,两个字节)
htonl()——主机字节序转换为网络字节序(long类型,四个字节)
ntohs()——网络字节序转换为主机字节序(short类型)
ntohl()——网络字节序转换为主机字节序(long类型)
为了程序的可移植性,当数据需要被传输到网络上时,一定要判断主机字节序,并确保其和网络字节序相同
如sockaddr_in结构体中的sin_port和sin_addr就都需要确保为网络字节序

socket相关函数

终于写到这里了。。。以下有些函数是客户端独有的,有些函数是服务器端独有的,为了行文方便,我就混着写了,读者可以参考前面的图示来确定这些函数执行的先后顺序

socket()

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domian,int type,int protocol);

一个一个参数来看
domain:需要设置为AF_INET,等同于sa_family和sin_family。
type:告诉内核,我们使用的socket是SOCK_STREAM还是SOCK_DGRAM类型(一般用不到SOCK_RAW类型)。
protocol:指使用的协议,设置为0时,是默认跟随type参数来选择协议,如果type参数值为SOCK_STREAM,protocol默认为IPPROTO_TCP;如果type值为SOCK_DGRAM,protocol默认为IPPROTO_UDP。
这个函数返回一个int类型套接字,我们后面要用这个套接字实现客户端和服务器的连接

bind()

#include <sys/types.h>
#include <sys/socket.h>
int bind(
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值