socket编程–相关函数
1.socket函数
头文件:
#include <sys/types.h>
#include <sys/socket.h>
1)函数原型
int socket(int domain, int type, int protocol);
2)函数功能
创建一个套接字,并返回一个文件描述符;
3)函数参数
domain
协议家族
AF_INET : IPv4协议
AF_INET6: IPv6协议
type:
数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字和 SOCK_DGRAM(数据报套接字/无连接的套接字)
protocol:
表示传输协议,常用的有 IPPROTO_TCP 和 IPPROTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
说明:
如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP,因此可以这样来调用 socket() 函数:
int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //IPPROTO_TCP表示TCP协议
这种套接字称为 TCP 套接字。
如果使用 SOCK_DGRAM 传输方式,那么满足这两个条件的协议只有 UDP,因此可以这样来调用 socket() 函数:
int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //IPPROTO_UDP表示UDP协议
这种套接字称为 UDP 套接字。
2. bind()函数
socket() 函数用来创建套接字,确定套接字的各种属性,然后服务器端要用 bind() 函数将套接字与特定的 IP 地址和端口绑定起来,只有这样,流经该 IP 地址和端口的数据才能交给套接字处理。
头文件
#include <sys/types.h>
#include <sys/socket.h>
1)函数原型
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
2)参数:
sockfd:socket 文件描述符
addr: sockaddr 结构体变量的指针
addrlen:addr变量的大小 可以使用sizeof()计算得出.
3)sockaddr_in 结构体
struct sockaddr_in{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
uint16_t sin_port(2); //16位的端口号
struct in_addr sin_addr(4); //32位IP地址
char sin_zero[8]; //不使用,一般用0填充
};
sin_family 和 socket() 的第一个参数的含义相同,取值也要保持一致
sin_prot 为端口号。uint16_t 的长度为两个字节,理论上端口号的取值
范围为 0~65536,但 0~1023 的端口一般由系统分配给特定的服务程序,
4) sockaddr结构体
struct sockaddr {
sa_family_t sa_family; //地址族(Address Family),也就是地址类型
char sa_data[14]; //IP地址和端口号
}
5)in_addr 结构体
sockaddr_in 的第3个成员是 in_addr 类型的结构体,该结构体只包含一个成员,如下所示:
struct in_addr{
in_addr_t s_addr; //32位的IP地址
};
为什么使用 sockaddr_in 而不使用 sockaddr?
sockaddr 与 sockaddr_in 的对比(括号中的数字表示所占用的字节数)
sockaddr 和 sockaddr_in 的长度相同,都是16字节,只是将IP地址和端口号合并到一起,用一个成员 sa_data 表示。要想给 sa_data 赋值,必须同时指明IP地址和端口号.,例如”127.0.0.1:80“,遗憾的是,没有相关函数将这个字符串转换成需要的形式,也就很难给 sockaddr 类型的变量赋值,所以使用 sockaddr_in 来代替。这两个结构体的长度相同,强制转换类型时不会丢失字节,也没有多余的字节。
3. connect函数
1)函数原型
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
2)函数的功能
建立连接;
3)函数参数
和bind()一样
4)返回值
连接成功,返回0,连接失败返回-1 errorno被设置.
4.listen函数
1)函数原型
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
2)函数功能
让套接字进入被动监听状态
3)函数参数
sockfd
需要进入监听状态的套接字
backlog
请求队列的最大长度。
请求队列
当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。
缓冲区的长度(能存放多少个客户端请求)可以通过 listen() 函数的 backlog 参数指定,但究竟为多少并没有什么标准,可以根据你的需求来定,并发量小的话可以是10或者20。
如果将 backlog 的值设置为 SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。
当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误,对于 Windows,客户端会收到 WSAECONNREFUSED 错误。
注意:listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept() 函数。
***4)返回值
连接成功,返回0,连接失败返回-1 errorno被设置
5.accept函数
1)函数原型
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
2)函数功能
当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求
3)函数参数
和bind(),connect()一样.
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sockfd 是服务器端的套接字.
listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
6 .write函数
两台计算机之间的通信相当于两个套接字之间的通信,在服务器端用 write() 向套接字写入数据,客户端就能收到,然后再使用 read() 从套接字中读取出来,就完成了一次通信。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
//向套接字中写入数据
fd 为要写入的文件的描述符,buf 为要写入的数据的缓冲区地址,count 为要写入的数据的字节数。
7 .read函数
1)函数原型
#include <unistd.h>
ssize_t read(int fd, void *buf,size_t count);
2)函数功能
从套接字中读取数据
fd 为要写入的文件的描述符,buf 为要写入的数据的缓冲区地址,count为要写入的数据的字节数。
read() 函数会从 fd 文件中读取 count 个字节并保存到缓冲区 buf,成功则返回读取到的字节数(但遇到文件结尾则返回0),失败则返回 -1。
8 其他相关函数
1)memset函数
#include <string.h>
void *memset(void *s, int c, size_t n);
//将目标字符串指定数目的字节置为 c
2)memcpy函数
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
//将指定数目的字节从源字节复制到目标字节
3)memcmp函数
#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
//比较两个字符串前n个字节大小 相同返回0,不同返回非零
4)inet_aton函数
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
//将cp指向的字符串转换成32位的网络字节序二级制值 ,结果存储在inp中字
//符串有效返回 1 否则 0
5)inet_pton函数
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
/*******************************************************
转换src为一个网络地址结构体
af:AF_INET
dst是struct in_addr
af:AF_INET6
dst是struct in_addr6
//尝试转换由src指向的字符串,并将二进制结果保存在dst中
******************************************************/
/******************************************************
*af :
AF_INET :
src指向一个点分十进制的网络地址 "ddd.ddd.ddd.ddd"
d的范围为0~255;
这个地址别转换成 struct in_addr类型,并且被复制到dst中
dst的大小必须是sizeof(struct in_addr)
AF_INET6:
指向一个IPv6的网络地址
格式是:x:x:x:x:x:x:x:x.每个x可以增加到4位16进制*
*******************************************************/
6)inet_ntop函数
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
//将src从数值格式转换成表达式格式,并存放在dst中.size是dst的大小
//af:地址家族,和inet_pton
//成功 返回一个指向dst的非空指针 出错 返回NULL errno被设置
/********************************************************
转换网络地址结构体src为字符串,转换的结果被复制到dst中,
size指定缓冲区的大小
********************************************************/