栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

二、Linux网络编程:Socket编程-接口

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

二、Linux网络编程:Socket编程-接口

2 Socket编程-接口 2.1 接口转换

转接口的换操作主要分为三类:字节序转换操作、IP地址转换操作和主机名转换操作。

2.1.1 字节序转换操作
  • 网络序转主机序
函数含义作用
ntohs()network to host short把unsigned short类型从网络序转换到主机序
ntohl()network to host long把unsigned long类型从网络序转换到主机序
  • 主机序转网络序
函数含义作用
htons()host to network short把unsigned short类型从主机序转换到网络序
htonl()host to network long把unsigned long类型从主机序转换到网络序

实例:

#include 
#include 
#include 
#include 
using namespace std;
void Hex(int n){
    char bytes[2];
    memcpy(bytes,&n,2);
    cout << showbase << setw(2) << setfill('0') << hex << (int)bytes[0] << setw(2) << (int)bytes[1] << endl;
}
int main(){
    uint16_t n;
    cin >> n;
    Hex(n);

    uint16_t m = htons(n);
    Hex(m);
    uint16_t t = ntohs(m);
    Hex(t);
}
[root@localhost socket]# ./a.out 
16
0x1000
000x10
0x1000
2.1.2 IP地址转换操作
函数功能特点
int inet_aton(const char *string, struct in_addr*addr)点分十进制数串转网络字节序长整型IPv4专用
in_addr_t inet_addr(const char* string)点分十进制数串转网络字节序长整型IPv4专用
char* inet_ntoa(struct in_addr addr)网络字节序长整型转点分十进制数串IPv4专用
int inet_pton(int af, const char *src, void *dst)点分十进制数串转网络字节序长整型IPv4/IPv6通用(推荐)
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)网络字节序长整型转点分十进制数串IPv4/IPv6通用(推荐)

实例:

#include 
#include 
#include 
using namespace std;
int main(){
    string s;
    cin >> s;
    in_addr addr;
    inet_aton(s.c_str(),&addr);
    cout << hex << addr.s_addr << endl;
    cout << inet_ntoa(addr) << endl;
    cout << inet_addr(s.c_str()) << endl;
}
[root@localhost socket]# ./a.out 
192.168.0.1
100a8c0
192.168.0.1
100a8c0
  • 结构体
结构体功能特性
struct sockaddr套接字地址结构IPv4/IPv6通用
struct sockaddr_inIPv4套接字地址结构IPv4专用
struct in_addrIPv4地址结构IPv4专用
in_addr_tIPv4地址类型IPv4专用
struct sockaddr_in6IPv6套接字地址结构IPv6专用
  • 套接字地址结构
struct sockaddr {
    unsigned short sa_family; // 套接字地址簇类型,为AF_INET
    char sa_data[14];         // 套接字地址数据(14位的协议地址)
};
  • IPv4套接字地址结构
struct sockaddr_in{
    short sin_family;         // 套接字地址簇类型,为AF_INET
    unsigned short sin_port;  // 端口号,网络字节序
    struct in_addr sin_addr;  // IP地址,网络字节序
    unsigned char sin_zero[8];// 填充字节
};

sin_zero[8]用来保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等。

  • IPv4地址结构
struct in_addr {
    in_addr_t s_addr;
};
  • IPv4地址类型
typedef unsigned int in_addr_t;
  • IPv6套接字地址结构
struct sockaddr_in6{
   uint8_t sin6_len;           //IPv6 为固定的24 字节长度
   sa_family_t sin6_family;    //套接字地址簇类型,为AF_INET6
   in_port_t sin6_port;        //16 位端口号,网络字节序
   uint32_t sin6_flowinfo;     //32 位流标签
   struct in6_addr sin6_addr;  //128 位IP地址
}
2.1.3 主机名转换操作
函数功能
struct hostent *gethostbyname(const char *hostname)主机名转地址
struct hostent *gethostbyaddr(const char * addr, int len, int type)地址转主机名
struct hostent *gethostbyaddr(const char * addr, int len, int type)地址转主机名
  • 主机名字和地址信息struct hostent
参数含义
h_name主机名字
h_aliases以空指针结尾的主机别名队列
h_addrtype地址类型。AF_INET/AF_INET6
h_length地址长度。在AF_INET类型地址中为4
h_addr第一个IP地址
h_addr_list以空指针结尾的IP地址的列表
2.2 socket操作 2.2.1 创建
int socket(int domain, int type, int protocol)
  • 参数
参数含义
domain协议域 AF_INET:IPv4;AF_INET6:IPv6;AF_LOCAL:Unix域
type类型 SOCK_STREAM:流式套接字;SOCK_DGRAM:数据报套接字;SOCK_RAW:原始套接字
protocol协议 0:自动根据type匹配协议;IPPROTO_TCP/IPPROTO_UDP
  • 返回值
返回值含义
-1失败
>0socket描述符
2.2.2 关闭
int close(int sockfd)
int shutdown(int sockfd,int howto)
  • 参数
参数含义
sockfd socket套接字
howto关闭方式
  • 关闭方式
方式值含义
SHUT_RD0关闭连接的读
SHUT_WR1关闭连接的写
SHUT_RDWR2连接的读和写都关闭
2.2.3 属性
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen)
  • 参数
参数含义
sockfd套接字描述符
level选项层次
optname选项
optval选项值指针
optlenoptval缓冲区长度
  • 选项层次
参数含义
SOL_SOCKET通用套接字选项
IPPROTO_TCPTCP选项
IPPROTO_IPIP选项
IPPROTO_IPV6IPv6选项

选项分为SOL_SOCKET级别和IPPROTO_IP级别两个级别

  • 返回值
返回值含义
-1失败
0成功
2.2.4 获取
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen)
  • 参数
参数含义
sockfd套接字描述符
level选项层次
optname选项
optval选项值指针
optlenoptval缓冲区长度
  • 返回值
返回值含义
-1失败
0成功
2.2.5 绑定
int bind(int socket, const struct sockaddr* address, socklen_t address_len)
参数含义
socket套接字描述符
address地址和端口号
address_lenaddress缓冲区的长度
  • 返回值
返回值含义
0成功
SOCKET_ERROR失败
2.2.6 监听
int listen(int sockfd, int backlog)
  • 参数
参数含义
sockfd监听的socket描述符
backlog排队的最大连接个数
  • 返回值
返回值含义
-1失败
0成功
2.2.7 连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数
参数含义
sockfd客户端的socket描述字
addr服务器的socket地址
addrlen服务器的socket地址的长度
  • 返回值
返回值含义
-1失败
0成功
2.2.8 接受
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
  • 参数
参数含义
sockfd服务器的socket描述符,监听socket描述符
addr客户端的socket地址
addrlen客户端的socket地址的长度
  • 返回值
返回值含义
-1失败
非-1连接描述符

注:说明
如果不需要获取客户端的套接字地址,后两个参数可设置为NULL。

 int connfd = accept(listenfd,NULL,NULL);

如果需要获取,则按照如下方式设置:

struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t remote_addr_len = sizeof(remote_addr);
int connfd = accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
2.2.9 发送

方式1:

ssize_t write(int fd, const void *buf, size_t len);
  • 参数
参数含义
fd文件描述符
buf写入数据
len写入数据的长度
  • 返回值
返回值含义
>0实际所写的字节数
<0出错

方式2:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • 参数
参数含义
sockfdsockfd文件描述符
buf写入数据
len写入数据的长度
flags通常为0
  • 返回值
返回值含义
>0实际所写的字节数
<0出错

方式3:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
  • 参数
参数含义
sockfdsockfd文件描述符
buf写入数据
len写入数据的长度
flags通常为0
dest_addr目标socket地址
addrlen目标socket地址长度
  • 返回值
返回值含义
>0实际所写的字节数
<0出错
2.2.10 接收

方式1:

ssize_t read(int fd, void *buf, size_t len);
  • 参数
参数含义
fd文件描述符
buf读取数据
len读取数据的长度
  • 返回值
返回值含义
0读到文件的结束
>0实际所写的字节数
<0出错

方式2:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • 参数
参数含义
fd文件描述符
buf读取数据
len读取数据的长度
flags通常为0
  • 返回值
返回值含义
0读到文件的结束
>0实际所写的字节数
<0出错

方式3:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen)
  • 参数
参数含义
fd文件描述符
buf读取数据
len读取数据的长度
flags通常为0
dest_addr目标socket地址
addrlen目标socket地址长度
  • 返回值
返回值含义
0读到文件的结束
>0实际所写的字节数
<0出错
2.3 客户端与服务器的通信 2.3.1 特殊地址设置
特殊地址ipv4ipv6
通配地址in_addr.sin_addr.s_addr = htonl(INADDR_ANY)in6_addr.sin6_addr=in6addr_any
回环地址in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK)in6_addr.sin6_addr=in6addr_loopback
广播地址in_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST)无广播
2.3.2 原则
  • 客户端/发送端
    必须指定连接/发送的IP(广播地址、回环地址或者某个具体地址)。
    必须指定连接/发送的port。
  • 服务器/接受端
    IP指定为通配地址、回环地址或者某个具体地址。
    必须指定绑定监听/接受的port。

Linux查看网络连接状态指令
netstat :查看网络连接状态、socket端口打开状态

选项作用
-antp查看tcp的状态
-anup查看udp的状态
2.3.3 TCP基本流程

C/S函数
Serversocket()、bind()、listen()、accept()、recv()/read()andsend()/write()、close()
Clientsocket()、connect()、send()/write()andrecv()/read()、close()
  • 客户端流程:4个步骤
int main(){
    //1.打开套接字
    fd = socket();
    
    //2.连接服务器
    connect(fd,...);
    
    //3.写入/读取数据
    write(fd,...);
    read(fd,...);   
    
    //4.关闭套接字
    close(fd);
}
  • 详细代码:
#include 
#include 
#include 
#include 
#include 
using namespace std;
int main(){
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(-1 == fd){
    	perror("open socket error");
	return 1;
    }
    //设置套接字
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton("127.0.0.1",&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(8080);//addr的端口号 = 主机序转化为网络序

    //2.连接服务器
    int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
    if(-1 == res){
    	perror("connect error");
	return 1;
    }
    
    //3.写入/读取数据
    string s;
    cin >> s;
    write(fd,s.c_str(),s.size()+1);
    char buffer[256] = {0};
    read(fd,buffer,256);
    cout << buffer << endl;
    
    //4.关闭套接字
    close(fd);
}
  • 服务端流程:6个步骤
int main(){
    //1.打开套接字
    fd = socket();

    //2.绑定
    bind(fd,...);

    //3.监听
    listen(fd,backlog);

    //4.连接客户端
    connfd = accept(fd,...);

    //5.读写数据
    read(connfd);
    write(connfd);

    //6.关闭套接字
    close(connfd);
    close(fd);
}
  • 详细代码:
#include 
#include 
#include 
#include 
#include 
using namespace std;
int main(){
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(-1 == fd){
    	perror("open socket error");
	return 1;
    }
    //编辑套接字信息
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton("127.0.0.1",&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(8080);//addr的端口号 = 主机序转化为网络序

    //2.绑定
    int res = bind(fd,(sockaddr*)&addr,sizeof(addr));
    if(-1 == res){
    	perror("bind error");
	return 1;
    }
    //3.监听
    res = listen(fd,4);
    if(-1 == res){
    	perror("listen error");
	return 1;
    }

    //4.连接客户端
    int connfd = accept(fd,NULL,NULL);
    if(-1 == connfd){
    	perror("accept error");
	return 1;
    }

    //5.读写数据
    char buffer[256] = {0};
    read(connfd,buffer,256);
    cout << buffer << endl;
    string s;
    cin >> s;
    write(connfd,s.c_str(),s.size()+1);

    //6.关闭套接字
    close(connfd);
    close(fd);
}

执行结果:

[root@localhost socket]# ./client 
zjl
123
...
...
...
[root@localhost socket]# ./sever 
zjl
123

步骤:执行./sever开启服务器,执行./client开启客户端,客户端向服务器发送数据,服务器接收到之后从服务器端向客户端发送数据。

2.3.4 UDP基本流程

2.3.4.1 单播

基本流程

  • 发送者

(1)打开socket

connfd = socket(AF_INET,SOCK_DGRAM,0)  

(2)设置发送地址和端口

struct sockaddr_in si;  
si.sin_family = AF_INET;  // 套接字地址簇,一般使用AF_INET
si.sin_port = htons(端口); // 16位端口,网络序
si.sin_addr.s_addr = inet_addr(IP地址); // IP地址,网络序

(3)发送数据

sendto(connfd,buf,buf_size,0,(struct sockaddr *)&si,sizeof(si));  

(4)关闭socket

close(connfd); 
  • 接收者

(1)打开socket

int connfd = socket(AF_INET, SOCK_DGRAM, 0);  

(2)设置接收地址和端口

struct sockaddr_in  si;  
si.sin_family = AF_INET;  // 套接字地址簇,一般使用AF_INET
si.sin_port = htons(端口); // 16位端口,网络序
si.sin_addr.s_addr = INADDR_ANY;   // INADDR_ANY表示接收来自任意IP、任意网卡的发给指定端口的数据

(3)端口绑定

int ret = bind(connfd, (struct sockaddr *)&si, sizeof(si));

(4)接受数据

recv(connfd,buf,buf_size,0);  

(5)关闭socket

close(connfd); 

实例:

  • unicast_send
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
 
#define BUFFER_SIZE 1024
 
int main(int argc,char **argv) {
    if(4 != argc){
        printf("usage:%s   n",argv[0]);
    }
    char *ip=argv[1];        //目的主机IP地址
    int port = atoi(argv[2]);//目的主机端口号

    // 1. 打开socket
    int connfd= socket(PF_INET,SOCK_DGRAM,0);
    assert(udpfd >= 0);

    // 2. 设置发送地址和端口
    struct sockaddr_in address;     //目的主机地址 
    bzero(&address,sizeof(address));    
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    inet_pton(AF_INET,ip,&address.sin_addr);
   
    // 3. 发送数据
    sendto(connfd,argv[3],strlen(argv[3])+1,0,(struct sockaddr *)&address,sizeof(address));
  
    // 4. 关闭socket
    close(connfd); 
    return 0;
}
  • unicast_recv
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
 
#define BUFFER_SIZE 1024
 
int main(int argc,char **argv) {
 
    if(3 != argc) {
        printf("usage:%s   n",argv[0]);
    }
 
    char *ip = argv[1];                 //用户自身的IP
    int port = atoi(argv[2]);          //服务器端口

    char buf[BUFFER_SIZE];

    // 1. 打开socket
    int connfd = socket(PF_INET,SOCK_DGRAM,0);
    assert(client_fd >= 0);

    // 2. 设置接收地址和端口
    struct sockaddr_in address,addr;
    socklen_t len = sizeof(addr);
    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    inet_pton(AF_INET,ip,&address.sin_addr);
 
    // 3. 端口绑定
    int ret = bind(connfd,(struct sockaddr *)&address,sizeof(address));
    assert(ret != -1);
 
    // 4. 接受数据
    printf("recv datan");
    ret = recvfrom(connfd,buf,BUFFER_SIZE-1,0,(struct sockaddr *)&addr,&len);
    buf[ret] = '';
    printf("n%sn",buf);
 
    // 5. 关闭socket
    close(connfd); 
    return 0;
}
2.3.4.2 组播/多播

基本流程

  • 发送者
    与单播发送者一致

  • 接收者

(1)打开套接字

int socket(int domain, int type, int protocol);   返回套接字

(2)构建服务器地址结构

struct sockaddr_in serveraddr;
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;                        
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP        
serveraddr.sin_port = htons(SERVER_PORT);//端口

(3)绑定地址

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

(4)构建组播属性结构

struct ip_mreqn group;
inet_pton(AF_INET,GROUP,&group.imr_multiaddr);//设置组播地址
inet_pton(AF_INET,"0.0.0.0",&group.imr_address);//设置本地地址
group.imr_ifindex=if_nametoindex("ent0");//设置网卡接口

(5)设置组播权限和属性

setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&group,sizeof(group));//设置组播权限及选项

(6)设置客户端组播地址

struct sockaddr_in cliaddr;
bzero(&cliaddr,sizeof(cliaddr));
cliaddr.sin_family=AF_INET;
inet_pton(AF_INET,GROUP,&cliaddr.sin_addr.s_addr);
cliaddr.sin_port=htons(CLIENT_PORT);

(7)发送数据

sendto(sockfd,buf,strlen(buf),0,(structsockaddr*)&cliaddr, sizeof(cliaddr));//往组播地址发送信息,返回数据大小

(8)关闭套接字

close(fd);

实例:

  • multicast_recv.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
void addroute(int connfd,const char* multi_ip,const char* dev){
    struct rtentry rt;
    memset(&rt,0,sizeof(rt));
 
    struct sockaddr_in* addr = (struct scokaddr_in*)&rt.rt_gateway;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = 0;
 
    addr = (struct scokaddr_in*)&rt.rt_dst;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = inet_addr(multi_ip);
 
    addr = (struct scokaddr_in*)&rt.rt_genmask;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = 0xFFFFFFFF;
    rt.rt_flags = RTF_UP|RTF_HOST;
    rt.rt_dev = dev;
 
    if(ioctl(connfd,SIOCADDRT,&rt)){
        perror("ioctl err");
        close(connfd);
    }
}
int main(int argc, char *argv[]) {
    if(5 != argc){
        printf("usage:%s    n",argv[0]);
        return 1;
    }
    int connfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(connfd < 0){
        perror("socket error");
        return 1;
    } 
    // 有些系统可能需要在路由中添加组播地址
    // addroute(sock,argv[2],argv[4]);
    int reuse = 1;
    if(setsockopt(connfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0){
        perror("setsockopt SO_REUSEADDR error");
        close(connfd);
        return 1;
    } 
 
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    addr.sin_port = htons(atoi(argv[3]));
 
    if(bind(connfd, (struct sockaddr*)&addr, sizeof(addr))){
        perror("bind error");
        close(connfd);
        return 1;
    }
   
    struct ip_mreq group;
    group.imr_multiaddr.s_addr = inet_addr(argv[2]);
    group.imr_interface.s_addr = inet_addr(argv[1]);
    if(setsockopt(connfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0){
        perror("Adding multicast group error");
        close(connfd);
        return 1;
    } 
   
    char buf[BUFSIZ];
    if(read(connfd, buf, sizeof(buf)-1) < 0){
        perror("read error");
        close(connfd);
        return 1;
    } 
    printf("recv:%sn",buf);
     
    close(connfd);
    return 0;
}
2.3.4.3 广播

基本流程

  • 发送者

(1)打开socket

cfd = socket(AF_INET,SOCK_DGRAM,0) :

(2)打开广播

setsockopt(cfd,SOL_SOCKET,SO_BROADCAST,&n,sizeof(n));  

参数n: 0表示关闭属性,非0表示打开属性

(3)设置发送地址和端口

struct sockaddr_in si;  
si.sin_family = AF_INET;  
si.sin_port = htons(端口);  
si.sin_addr.s_addr = inet_addr("255.255.255.255");

(4)发送数据

sendto(cfd,buffer,buffer_size,0,(struct sockaddr *)&si,sizeof(si));  

(5)关闭socket

close(cfd); 
  • 接收者

与单播发送者一致

实例:

  • boardcast_send.c
#include   
#include  
#include  
#include   
#include   
#include   
 
int main(int argc,char* argv[]) {  
    if(4 != argc){
        printf("usage:%s   n",argv[0]);
        return 1;
    }
    int connfd= socket(AF_INET,SOCK_DGRAM, 0);
    if (-1 == connfd) {  
        perror("socket error");  
        return 1;  
    }  
 
    int opt  = 1;
    if(setsockopt(connfd,SOL_SOCKET,SO_BROADCAST,(char*)&opt,sizeof(opt))==-1) {
        perror("setsockopt fail.");
        close(connfd);
        return 1;  
    }
 
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;  
    addr.sin_addr.s_addr = inet_addr(argv[1]);  
    addr.sin_port = htons(atoi(argv[2]));
 
    if (-1 == sendto(connfd, argv[3], strlen(argv[3])+1, 0, (struct sockaddr *)&addr, sizeof(addr))) {  
        perror("sendto error");  
        close(connfd); 
        return 1;  
    }  
     
    printf("send ok!n");        
    close(connfd); 
    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/939749.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号