Linux socket编程
套接字定义描述
套接字的域
- AF_INET ====>IPv4
- AF_INET6 ====>IPv6
- AF_UNIX ====>unix 域
- AF_UPSPEC ====>未使用
套接字的类型
- SOCK_DGRAM ====>固定长度,无链接的,不可靠的报文传递
- SOCK_RAM ====>IP协议数据报接口
- SOCK_SEQPACKET====>固定长度,有序,可靠的,面向连接的报文传递
- SOCK_STREAM ====>有序的,可靠的,双向的,面向连接的字节流
协议类型
- IPPROTO_IP ====>IPV4协议族
- IPPROTO_IPV6 ====>IPV6协议族
- IPPROTO_ICMP ====>控制报文协议
- IPPROTO_RAM ====>原始IP数据包
- IPPROTO_TCP ====>字节流传输控制协议
- IPPROTO_UDP ====>用户数据包协议
API
头文件
#include <sys/socket.h>
字节序
以太网中的字节序是大端的,因此相关的以太网的数据在小端机器上需要使用相关接口转换为以太网字节序。常用转换工具函数:
//4字节整形变量主机字节序的转换为网络字节序
uint32_t htonl(uint32_t hostint32);
//2字节整形变量主机字节序的转换为网络字节序
uint16_t htons(uint15_t hostint16);
//和上面相反
uint32_t ntohl(uint32_t netint32);
//和上面相反
uint32_t ntohl(uint32_t netint32);
常用接口(具体使用man 2 xxx 命令查看具体说明)
//创建套接字
int socket(int domain,int type,int protocol);
//将一个套接字和指定地址和端点关联起来
int bind(int sockfd,const struct sockaddr * addr,socklen_t addrlen);
//开始监听这个套接字
int listen(int socket, int backlog);
//向指定socket发起连接请求
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//接收监听到的一个socket连接请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//通过socket 发送数据,地址是socket默认
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//通过socket 接收数据,地址为socket 默认
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//通过socket 发送数据,地址是由参数指定
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
//通过socket 接收数据,地址由参数指定
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
//常用在unix 域下的进程通讯,发送数据
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
//常用在unix 域下的进程通讯,接收数据
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
//获取当前socket信息
int getsockname((int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//获取原端socket信息(建立连接之后)
int getpeername((int sockfd, const struct sockaddr *addr, socklen_t addrlen);
关闭套接字
套接字关闭可以像关闭一个文件的描述符一样调用close()接口来关闭socket接口,除此之外还可以使用shutdown接口进行更加细致的关闭操作:
sockfd :sock描述符
how :SHUT_RDWR 读写两端都关闭
: SHUT_WR 关闭写端
: SHUT_RD 关闭读端
返回值 :成功返回0 失败返回-1
int shutdown(int sockfd,int how);
相关结构体
//描述网络信息
struct sockaddr_in {
s_add.sin_family = AF_INET;
s_add.sin_port = htons(1040);
s_add.sin_addr.s_addr = inet_addr("127.0.0.1");
};
TCP服务端配置过程
- 建立套接字,返回套接字
//建立 IPv4域下的字节流服务的TCP协议的socket int sockfd_s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- 将套接字和指定地址关联,使用上述的结构体描述端口和地址信息。
//将这个socket个特定地址何端口关联起来 bind(sockfd_s,(struct sockaddr*)&s_add,sizeof(struct sockaddr_in))
- 告诉系统套接字刻意监听链接。
//开始监听 listen(sockfd_s,1)
- 接受一个连接,这一步将返回新的套接字接口
//接受任意连接请求,返回可以用来数据传输的socket描述符 accept(sockfd_s,NULL,NULL);
TCP客户端配置过程
- 建立套接字,返回套接字
//建立 IPv4域下的字节流服务的TCP协议的socket int sockfd_s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- 连接到指定地址何端口
//向制定地址和端口发起链接请求 connect(sockfd_c,(struct sockaddr*)&c_add,sizeof(c_add))
示例代码
见文末
连接信息获取
获取本地套接字信息
获取远端套接字信息(连接后)
套接字选项
- 选项 类型(非特殊为int型) 描述
- SO_ACCEPTCONN 查询套接字是否可以被监听
- SO_BROADCAST 非0 广播数据报
- SO_DEBUG 非0 启用调试
- SO_ERROR 返回挂起套接字的错误并清除
- SO_KEEPALIVE 非0 启用keep-alive报文
- SO_OOBINLINE TCP 带外数据
- SO_RCVBUF 获取接收缓冲区的数据
- SO_RCVLOWAT 接受调用返回的最小字节数
- SO_RCVTIMEO struct timeval 接收调用的超时时间
- SO_REUSEADDR 重用地址,有用
- SO_SNDBUF 发送buf中的字节数
- SO_SNDLOWAT 发送传送的最小字节数
- SO_SNDTIMEO struct timeval 发送超时时间
- SO_TYPE 套接字类型标识,仅get可用
设置选项
int setsockopr(int sockfd,int level,int option,void *val,socklen_t len);
获取选项
int setsockopr(int sockfd,int level,int option,void *val);
带外数据
是一些通讯协议支持的可选功能,与普通数据相比其具有更高的优先级被传输。目前TCP的支持带外数据传输的,仅支持一个字节的紧急数据。有些实现TCP的带外数据还可以产生SIGURG信号。还可以通过下面的接口判断是否有紧急数据,当有紧急数据时,返回1。
iny sockatmark(int sockfd);
异步socket和非阻塞socket
基本上socket的异步IO和阻塞和非阻塞的处理和普通的文件基本相同,但是有一些区别的是,但是目前还没有完整的标准,视具体平台的实现情况。
示例demo
int main(int argc,int *argv)
{
char *name,i;
struct hostent* host;
size_t len = sysconf(_SC_HOST_NAME_MAX);
if(len>0){
name = malloc(len+1);
}else{
name = malloc(1024);
len = 1024;
}
printf("Buff size%ld
",stdout->_IO_buf_end-stdout->_IO_buf_base);
if(gethostname(name,len)<0){
printf("gethostname error!
");
}
host = gethostbyname(name);
if(*host->h_aliases){
printf("%s
%s
%s
%d
",
host->h_name,//host name
*host->h_aliases, //host_name aliases
(host->h_addrtype==AF_INET?"IP_V4":"IP_V6"),
host->h_length);
}else{
printf("%s
%s
%d
",
host->h_name,
(host->h_addrtype==AF_INET?"IP_V4":"IP_V6"),
host->h_length);
}
for(i=0;host->h_addr_list[i] != NULL;i++){
printf("%s
",inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
}
printf("befor the fork call
");
int pid = fork();
if(pid){
int status,temp=1;
int sockfd_s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd_s<0){
goto fail;
}
if(setsockopt(sockfd_s,SOL_SOCKET,SO_REUSEADDR,&temp,sizeof(int)) < 0)
{
perror("setsockopt");
}
struct sockaddr_in s_add;
s_add.sin_family = AF_INET;
s_add.sin_port = htons(1040);
s_add.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_pton(AF_INET,"127.0.0.1",&s_add.sin_addr);
if(bind(sockfd_s,(struct sockaddr*)&s_add,sizeof(s_add))<0){
printf("1
");
goto fail;
}
if(listen(sockfd_s,1)<0){
goto fail;
}
int c_fd = accept(sockfd_s,NULL,NULL);
if(c_fd < 0){
goto fail;
}
int len=sizeof(s_add);
if(getpeername(c_fd,(struct sockaddr*)&s_add,&len)<0){
printf("2
");
goto fail;
}
printf("server:
%s
%d
",inet_ntoa(s_add.sin_addr),ntohs(s_add.sin_port));
if(c_fd){
while(1){
char buf[64];
recv(c_fd,buf,64,0);
printf("%s
",buf);
sleep(1);
}
}
fail:
close(sockfd_s);
perror(strerror(errno));
kill(pid,9);
waitpid(pid,&status,0);
printf("server end
");
return 0;
}else if(!pid){
sleep(3);
int sockfd_c = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd_c<0){
goto fail_c;
}
struct sockaddr_in c_add;
c_add.sin_family = AF_INET;
c_add.sin_port = htons(1040);
c_add.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_pton(AF_INET,"127.0.0.1",&c_add.sin_addr);
if(connect(sockfd_c,(struct sockaddr*)&c_add,sizeof(c_add))){
goto fail_c;
}
struct sockaddr_in d_add;
int len=sizeof(d_add);
if(getpeername(sockfd_c,(struct sockaddr*)&d_add,&len)<0){
goto fail_c;
}
printf("cliens
%s
%d
",inet_ntoa(d_add.sin_addr),ntohs(d_add.sin_port));
while(1)
{
send(sockfd_c,"hello word!
",strlen("hello word!
"),0);
sleep(1);
}
fail_c:
close(sockfd_c);
perror(strerror(errno));
_exit(0);
}
return 0;
}