TCP/IP网络编程基础

套接字类型

创建套接字

  • Linux
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
//成功时返回文件描述符,失败时返回-1

domain:套接字中使用的协议族信息。
type:套接字数据传输类型信息。
protocol:计算机间通信中使用的协议信息。

  • Windows
#include <winsock2.h>
int socket(int af,int type,int protocol);
//成功时返回socket句柄,失败时返回INVALID_SOCKET(本质为-1)
协议族(domain)

头文件sys/socket.h中声明的协议族

名称协议族
PF_INETIPv4互联网协议族
PF_INET6IPv6互联网协议族
PF_LOCAL本地通信的UNIX协议族
PF_PACKET底层套接字的协议族
PF_IPXIPX Novell协议族
套接字类型(type)
  • 套接字类型1:面向连接的套接字(SOCK_STREAM)
    特点:可靠的、按序传递的、基于字节的面向连接的数据传输方式的套接字。
  • 套接字类型2:面向消息的套接字(SOCK_DGRAM)
    特点:不可靠的、不按序传递的、以数据的高速传输为目的套接字。
协议的最终选择(protocol)

明确表示哪种协议:

int tcp_socket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
int udp_socket = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);

协议设置

地址族

地址信息的表示

  1. POSIX中定义的数据类型
数据类型名称数据类型说明声明的头文件
int8_tsigned 8-bit intsys/types.h
uint8_tunsigned 8-bit int(unsigned char)
int16_tsigned 16-bit int
uint16_tunsigned 16-bit int(unsigned short)
int32_tsigned 32-bit int
uint32_tunsigned 32-bit int(unsigned long)
sa_family_t地址族(address family)sys/socket.h
socklen_t长度(length of struct)
int addr_tIP地址,声明为unit32_t netinet/in.h
int port_t端口号,声明为unit16_t
  1. 基于IPv4地址的结构体
struct sockaddr_in
{
	sa_family_t 	sin_family;		//地址族(Address Family)
	uint16_t		sin_port;		//16位TCP/UDP端口号
	struct in_addr	sin_addr;		//32位IP地址
	char			sin_zero[8];	//不使用
}

struct in_addr
{
	in_addr_t		s_addr;			//32位IPv4地址
}
  1. 结构体sockaddr_in的成员属性

成员sin_family:

地址族(Address Family)含义
AF_INETIPv4网络协议中的使用的地址族
AF_INET6IPv6网络协议中的使用的地址族
AF_LOCAL本地通信中采用的UNIX协议的地址族

成员sin_port:端口号
成员sin_addr:IP地址
成员sin_zero:无特殊含义,只是为了使结构体socketaddr_in的大小与socketaddr结构体保持一致而插入的成员,必需填充为0,否则无法得到想要的结果。

数据序列

CPU向内存保存数据的方式有2种,这意味这CPU解析数据的方式也分为2种。

  • 大端序(Big Endian):高位字节存放到低位地址。
  • 小端序(Little Endian):高位字节存放到高位地址。

为了保证网络传输的一致性,在通过网络传输数据时约定统一方式,这种约定称为网络字节序,统一约定位大端序。即先把数据数组转化位大端序格式再进行网络传输。
注1:Intel和AMD系列的CPU都采用小端序标准。
注2:除了向socketaddr_in结构体变量填充数据外,其他情况都无需考虑字节序问题(这个过程时自动的)。

字节序转换

转换函数(两个平台都可以使用):

  • unsigned short htons(unsigned short):把short类型数据从主机字节序转化为网络字节序。
  • unsigned short ntohs(unsigned short):把short类型数据从网络字节序转化为主机字节序。
  • unsigned long htonl(unsigned long):把long类型数据从主机字节序转化为网络字节序。
  • unsigned long ntohl(unsigned long):把long类型数据从网络字节序转化为主机字节序。
    函数名命名规则:
  • htons中的h代表主机(host)字节序
  • htons中的n代表网络(network)字节序
  • htons中的s代表short,其中htonl中的l代表long(Linux中long类型占用4个字节)

IP字符串信息转换为网络字节序

  1. inet_addr函数
    将点分十进制表示法(例如211.214.107.99)的IP转换为32位整型数据(满足网络字节序),同时科研检测无效的IP地址:
#include <arpa/inet.h>
in_addr_t inet_addr(const char *string);
//成功时返回32位大端序整型值,失败时返回INADDR_NONE
  1. inet_aton函数
    该函数只存在于Linux之中,Windows中不存在,与inet_addr函数功能相同,只不过该函数利用了in_addr结构体,且其使用频率更高。
#include <arpa/inet.h>
int inet_aton(const char *string,struct in_addr *addr);
//成功时返回1(true),失败时返回0(false)

参数string:含有需转换的IP地址信息的字符串地址值。
参数addr:将保存转换结果的in_addr结构体变量的地址值。

注意:实际编程中若要调用inet_addr函数,需将转换后的IP地址信息代入sockaddr_in结构体中声明的in_addr结构体变量。而inet_atong函数则不需要此过程。原因在于,若传递in_addr结构体变量地址值,函数会自动把结果填入该结构体变量中。

inet_aton(addr,&addr_inet.sin_addr);

网络字节序转换为IP字符串信息

#include <arpa/inet.h>

char *inet_ntoa(struct in_addr adr);
//成功时返回转换的字符串地址值,失败时返回-1。

需要注意的是,函数返回值类型位char指针,返回字符串地址意味着字符串已保存到内存空间,但该函数未向程序员要求分配内存,而是再内部申请了内存并保存了字符串。也就是说,调用完该函数后,应立即将字符串信息复制到其他内存空间。

char *str_ptr;
char str_arr[20];
str_ptr = inet_ntoa(addr1.sin_addr);
strcpy(str_arr,str_ptr);

实例

网络地址初始化:

struct sockaddr_in addr;
char *serv_ip = "211.217.168.13";	//IP字符串
char *serv_port = "9190";			//端口字符串
memset(&addr,0,sizeof(addr));		//结构体变量addr各成员初始化为0
addr.sin_family = AF_INET;					//指定地址族
addr.sin_addr.s_addr = inet_addr(serv_ip);	//IP初始化
addr.sin_port = htons(atoni(serv_port));	//端口初始化

注:atoi用于将数字字符串转换成int输出。

常量INADDR_ANY可自动获取运行服务器端的计算机IP地址,不必亲自输入。

addr.sin_addr.s_addr = inet_addr(INADDR_ANY);	//IP初始化

WSAStringToAddress & WSAAddressToString

这两个函数在功能上与inet_ntoa和inet_addr完全相同,优点是支持多种协议,缺点是降低兼容性。
WASAStringToAddress函数(字符串转地址):

#include <winsock2.h>
INT WSAStringToAddress(
	LPTSTR AddressString,INT AddressFamily,LPWSAPROTOCOL_INFO lpProtocolInfo,LPSOCKADDR lpAddress,LPINT lpAddressLength
);
//成功时返回0,失败时返回SOCKET_ERROR
  • AddressString:含有IP和端口号的字符串地址值。
  • AddressFamily:第一个参数中地址所属的地址族信息。
  • lpProtocolInfo:设置协议提供者(Provider),默认为NULL。
  • lpAddress:保存地址信息的结构体变量地址值。
  • lpAddressLength:第四个参数中传递的结构体长度所在的变量地址值。
    WASAStringToAddress函数(字符串转地址):
#include <winsock2.h>
INT WSAAddressToString(
	LPTSTR lpsaAddress,DWORD dwAddressLength,LPWSAPROTOCOL_INFO lpProtocolInfo,LPSTR lpszAddressString,LPDWORD lpdwAddressStringLength
);
//成功时返回0,失败时返回SOCKET_ERROR
  • lpsaAddress:需要转换的地址信息结构体变量地址值。
  • dwAddressLength:第一个参数中结构体的长度。
  • lpProtocolInfo:设置协议提供者(Provider),默认为NULL。
  • lpszAddressString:保存转换结果的字符串地址值。
  • lpdwAddressStringLength:第四个参数中存有地址信息的字符串长度。
#undef UNICODE
#undef _UNICODE
#include <stdio.h>
#include <winsock2.h>

int main(int argc,char *argv[])
{
	char *strAddr = '203.211.218.102:9190';

	char strAddrBuf[50];
	SOCKADDR_IN servAddr;
	int size;
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2,),&wsaData);

	size = sizeof(servAddr);
	WSAStringToAddress(
		strAddr,AF_INET,NULL,(SOCKADDR*)&servAddr,&size
	);

	size = sizeof(strAddrBuf);
	WSAAddressToString(
		(SOCKADDR*)&servAddr,sizeof(servAddr),NULL,strAddrBuf,&size
	);
	printf("second conv result:%s\n",strAddrBuf);
	WSACleanup();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值