网络编程基础
一、Socket概述
Socket接口是TCP/IP网络的API,它定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应
用程序。常见的Socket有以下三种类型。
流式Socket(SOCK_STREAM): 提供可靠的、面向连接的通信流。使用TCP协议
数据报Socket(SOCK_DGRAM): 定义了一种无连接的服务,使用UDP协议,不保证传输的可靠性和数据的原
始顺序
原始Socket:允许对底层协议进行直接访问,如IP,它的功能强大,用户可以通过该Socket开发自己的
协议
二、基本数据结构和函数
1、网络地址
(1)结构体sockaddr
struct sockaddr{
unsigned short sa_family;//地址族,AF_xxx
char sa_data[14];//14字节的协议地址
};
sa_family: 一般为AF_INET,代表Internet(TCP/IP)地址族的IPV4协议
sa_data: 包含了一些远程计算机的IP地址、端口号和套接字的数目,这些数据是混杂在一起的
(2)结构体sockaddr_in
struct sockaddr_in{
short int sin_family; //地址族
unsigned short int sin_port;//端口号
struct in_addr sin_addr; //IP号
unsigned char sin_zero[8];
};
sin_zero:用来将结构体struct sockaddr_in结构填充到与struct sockaddr同样的长度,
可以用bzero()或memset()函数将其置为零
指向sockaddr_in的指针与指向sockaddr的指针可以相互转换。
2、IP地址转换
网络上的IP都是点分十进制的格式,如“192.168.0.1”,struct in_addr结构中用的是32位的IP,
如IP(C0A80001)是“192.168.0.1”,俩地址的转换函数:
int inet_aton(const char * cp, stuct in_addr * inp)//点分十进制转为32位的IP输入(inp)
返回值:成功——非零值 失败——0
char * inet_ntoa(struct in_addr in)//32位的IP转为点分十进制
返回值:成功——返回指向存储着点分十进制的静态缓冲区的指针 失败——NULL
3、IP和域名的转换
将域名转换为IP地址可以使用以下两个函数:
struct hostent * gethostbyname(const char * hostname);
功能:将机器名转换为一个结构指针,在这个结构里面储存了域名的信息
返回值:失败时返回NULL,且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息
struct hostent * gethostbyaddr(const char * addr, int len, int type);
len: 字节长度(4)
type: 族(AF_INET)
功能:将一个32位的IP地址转换为结构指针
返回值:失败时返回NULL,且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息
hostent结构体:
struct hostent{
char * h_name; //主机的正式名称
char * h_aliases; //主机的别名
int h_addrtype; //主机的地址类型AF_INET
int h_length; //主机的地址长度对于IP4是4字节32位
char ** h_addr_list; //主机的IP地址列表
};
#define h_addr h_addr_list[0]//主机的第一个IP地址
4、字节顺序转换
计算机数据存储有两种字节优先顺序:高位字节优先和地位字节优先。优先顺序不同会出现数据不一致
的情况,为了统一,linux中有专门的字节转换函数:
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unisgned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
h: host
n: network
s: short
l: long
5、服务信息函数(不常用)
在网络程序中有时需要知道端口、IP和服务信息。可以使用以下函数获得:
int getsockname(int sockfd, struct sockaddr * localaddr, int * addrlen);
int getpeername(int sockfd, struct sockaddr * peeraddr, int * addrlen);
struct servent * getservbyname(const char * servname, const char * protoname);
struct servent * getservbyport(int port, const char * protoname);
struct servent{
char * s_name; //正式服务名
char ** s_aliases; //别名列表
int s_port; //端口号
char * s_proto; //使用的协议
};
三、Socket编程基础
示例;(需要输入IP地址)
int main(int argc, char ** argv){ struct sockaddr_in addr; struct hostent * host; char ** alias; if(argc < 2){ fprintf(stderr,"Usage:%s hostname|ip..\n\a",argv[0]); exit(1); } argv++; for(; *argv!=NULL; argv++){ if(inet_aton(* argv, &addr.sin_addr)!=0){//将点分十进制转换为32位的IP存入addr.sin_addr host = gethostbyaddr((char *)&addr.sin_addr, 4, AF_INET); printf("Address information of IP %s\n", * argv); } else{ host = gethostbyname(* argv); printf("Address information of IP %s\n", * argv); } if(host == NULL){ fprintf(stderr, "No address information of %s\n", * argv); continue; } printf("Official host name %s\n",host->h_name);//显示主机名 printf("Name aliases:"); for(alias=host->h_aliases; * alias!=NULL;alias++)//显示所有别名 printf("%s,",*alias); printf("\nIP address:"); for(alias=host->h_addr_list; *alias!=NULL; alias++)//显示IP地址 printf("%s,",inet_ntoa(*(struct in_addr *)(*alias)));//将32位的IP转换为点分十进制 } }
三、TCP通信编程
1、创建套接字
int socket(int family, int type, int protocol)
family:协议或地址族
type:套接字类型(SOCK_STREAM,SOCK_DGRAM)
protocol: 协议号,0指定family和type的默认协议号
返回值:成功——返回一个×××Socket描述符,错误——返回-1并设置error
2、绑定本地地址
int bind(int sockfd, struct sockaddr * sa, int addrlen)
sockfd: 套接字描述符
sa: 指向struct sockaddr的指针,包含有关地址的信息(名称、端口和IP地址...)
addrlen: 套接字地址接口的长度,可以设置为sizeof(struct sockaddr)
返回值:成功——返回0, 失败——返回-1并设置error
bind函数将Socket与本机上的一个端口相关联,随后即可在该端口监听服务请求。
3、listen()函数
int listen(int sockfd, int backlog)
backlog: 未经处理的连接请求队列可以容纳的最大数目
listen()函数将套接字设置为监听模式,以等待连接请求。
4、accept()函数
int accept(int sockfd, struct sockaddr * addr, int * addrlen)
addr: 一般是一个指向struct sockaddr_in结构的指针,将在accept函数调用返回后填入远程连接过来的
计算机的信息,如远程计算机的IP地址和端口
addrlen:表示参数addr所占的内存区大小,在accept函数调用返回后填入返回的addr结构体的大小
accept()函数用于响应连接请求,建立连接并产生一个新的socket描述符来描述该连接,该连接用来与特定
的客户端交换信息,默认为阻塞函数。
5、connect()函数
int connect(int sockfd, struct sockaddr * serv_addr, int addrlen)
serv_addr:存储远程计算机的IP地址和端口信息的sockadddr结构
addrlen:是serv_addr结构体所占的内存大小
返回值:成功——返回0,错误——返回-1
connect()调用成功后,就可以使用sockfd作为服务器连接的套接字描述符,使用I/O函数进行数据传输了。
6、数据通信
(1)send()和recv()函数(最基本的、通过连接的套接字流进行通信的函数)
int send(int sockdfd, const void * msg, int len, int flags);
返回值:返回实际发送数据的长度,错误——返回-1
int recv(int sockfd, void * buf, int len, unsigned int flags);
返回值:返回真正收到数据的长度,错误——返回-1
sockfd: 与远程程序连接的套接字描述符
msg: 指向想发送或存储信息的地址
len: 信息的长度
flags:发送或接收标记,一般设为0
(2)sendto()和recvfrom()函数(进行无连接的UDP通信时使用)
int sendto(int sockfd, const void * msg, int len, unsigned int flags,
const struct sockaddr * to, int tolen);
int recvfrom(int sockfd, void * buf, int len, unsigned int flags,
truct sockaddr * from, int fromlen);
(4)使用close()函数关闭当前的连接
int close(int sockfd)