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

2023-11-17

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 <iostream>
#include <cstring>
#include <iomanip>
#include <arpa/inet.h>
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 <iostream>
#include <cstring>
#include <arpa/inet.h>
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_in IPv4套接字地址结构 IPv4专用
struct in_addr IPv4地址结构 IPv4专用
in_addr_t IPv4地址类型 IPv4专用
struct sockaddr_in6 IPv6套接字地址结构 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 失败
>0 socket描述符

2.2.2 关闭

int close(int sockfd)
int shutdown(int sockfd,int howto)
  • 参数
参数 含义
sockfd socket 套接字
howto 关闭方式
  • 关闭方式
方式 含义
SHUT_RD 0 关闭连接的读
SHUT_WR 1 关闭连接的写
SHUT_RDWR 2 连接的读和写都关闭

2.2.3 属性

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen)
  • 参数
参数 含义
sockfd 套接字描述符
level 选项层次
optname 选项
optval 选项值指针
optlen optval缓冲区长度
  • 选项层次
参数 含义
SOL_SOCKET 通用套接字选项
IPPROTO_TCP TCP选项
IPPROTO_IP IP选项
IPPROTO_IPV6 IPv6选项

选项分为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 选项值指针
optlen optval缓冲区长度
  • 返回值
返回值 含义
-1 失败
0 成功

2.2.5 绑定

int bind(int socket, const struct sockaddr* address, socklen_t address_len)
参数 含义
socket 套接字描述符
address 地址和端口号
address_len address缓冲区的长度
  • 返回值
返回值 含义
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);
  • 参数
参数 含义
sockfd sockfd文件描述符
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);
  • 参数
参数 含义
sockfd sockfd文件描述符
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 特殊地址设置

特殊地址 ipv4 ipv6
通配地址 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.4 TCP基本流程

在这里插入图片描述

C/S 函数
Server socket()bind()listen()accept()recv()/read()andsend()/write()close()
Client socket()connect()send()/write()andrecv()/read()close()
  • 客户端流程:4个步骤
int main(){
    //1.打开套接字
    fd = socket();
    
    //2.连接服务器
    connect(fd,...);
    
    //3.写入/读取数据
    write(fd,...);
    read(fd,...);   
    
    //4.关闭套接字
    close(fd);
}
  • 详细代码:
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
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 <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
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.4.1 实例1:采用父子进程的方式

  • 客户端
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    //2.连接服务器
    int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
    if(-1 == res) {
        perror("connect error");
        return 1;
    }
    //3.读写数据
    if(fork()) {
        string s;
        while(cin >> s) {
            write(fd,s.c_str(),s.size()+1);
        }
    } else {
        for(;;) {
            char buffer[256] = {0};
            read(fd,buffer,256);
            cout << buffer << endl;
        }
    }
    //4.关闭套接字
    close(fd);
}
  • 服务器
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//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.读写数据
    if(fork()) {
        for(;;) {
            char buffer[256] = {0};
            read(connfd,buffer,256);
            cout << buffer << endl;
        }
    } else {
        string s;
        while(cin >> s) {
            write(connfd,s.c_str(),s.size()+1);
        }
    }
    //6.关闭套接字
    close(connfd);
    close(fd);
}
[root@localhost 2]# ./client 127.0.0.1 8080
as
as
1
2
3
4
45
q
a
s
d
a
...
...
[root@localhost 2]# ./sever 127.0.0.1 8080
as
as
1
2
3
4
45
q
a
s
d
a

2.4.2 实例2:采用C++多线程的方式

  • 客户端
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <thread>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    //2.连接服务器
    int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
    if(-1 == res) {
        perror("connect error");
        return 1;
    }
    //3.读写数据
    thread t([fd](){
        for(;;) {
            char buffer[256] = {0};
            int n = read(fd,buffer,256);
	    if(0 == n){
	    	cout << "sever exit!" << endl;
		break;
	    }
            cout << buffer << endl;
        }
    });    
    t.detach();
    string s;
    while(cin >> s) {
       write(fd,s.c_str(),s.size()+1);
    }
    //4.关闭套接字
    close(fd);
}
  • 服务器
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <thread>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//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.读写数据
    thread t([connfd]() {
        for(;;) {
            char buffer[256] = {0};
            int n = read(connfd,buffer,256);
            if(0 == n) {
                cout << "client exit!" << endl;
                break;
            }
            cout << buffer << endl;
        }
    });
    t.detach();
    string s;
    while(cin >> s) {
        write(connfd,s.c_str(),s.size()+1);
    }
    //5.关闭套接字
    close(connfd);
    close(fd);
}
[root@localhost 2]# ./client 127.0.0.1 8081
asd
1
2
2
3
4
a
v
d

d
^C
...
...
[root@localhost 2]# ./sever 127.0.0.1 8081
asd
1 
2
2
3
4
a
v
d
d
client exit!
^C

2.4.3 实例3:多个客户端和服务器之间的通信

  • 客户端
    同上
  • 服务器
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <thread>
#include <vector>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

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

    //向多个客户端发送
    vector<int> fds;
    thread writer([&]() {
        string s;
        while(cin >> s) {
            for(auto connfd:fds) {
                write(connfd,s.c_str(),s.size()+1);
            }
        }
    });
    writer.detach();

    for(;;) {
        sockaddr_in remote_addr;
        socklen_t len = sizeof(remote_addr);
        //4.打开连接套接字
        int connfd = accept(fd,(sockaddr*)&remote_addr,&len); //block
        cout << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl; //打印哪个客户端连接服务器
        if(-1 == connfd) {
            perror("accept error");
            return 1;
        }
        fds.push_back(connfd);
        cout << "accept" << endl;

        //5.读写数据
        thread t([connfd]() {
            for(;;) {
                char buffer[256] = {0};
                int n = read(connfd,buffer,256);
                if(0 == n) {
                    cout << "client exit!" << endl;
                    break;
                }
                cout << buffer << endl;
            }

        });
        t.detach();
    }
    //5.关闭套接字
    for(auto connfd:fds) close(connfd);
    close(fd);
}
[root@localhost 2]# ./sever 0.
argument error
Usage:./seversever_ip sever_port
[root@localhost 2]# ./sever 0.0.0.0 8080
bind
listen
127.0.0.1:33558
accept
1
127.0.0.1:42390
accept
3
127.0.0.1:54876
accept
hello
cccc
client exit!
client exit!
client exit!

...
[root@localhost 2]# ./client 127.0.0.1 8080
1   
cccc
...
[root@localhost 2]# ./client 127.0.0.2 8080
3
cccc
...
[root@localhost 2]# ./client 127.0.0.8 8080
hello
cccc

结果分析:同时打开多个客户端,向服务器发送消息都能收到。服务器发送的消息每个客户端都能收到,且客户端连接和退出时,服务器都能打印相关信息。

2.4.4 实例4:客户端从服务器下载文件

  • file_sever.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <thread>
#include <vector>
using namespace std;
int main(int argc,char* argv[]) {
    if(4 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port path\n" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

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

    int connfd = accept(fd,NULL,NULL);
    if(-1 == connfd){
    	perror("accept error");
		return 1;
    }
    cout << "accept" << endl;

    char buffer[256] = {0};
    read(connfd,buffer,256);
    cout << buffer << endl;
    //请求文件的路径
    string file = string(argv[3])+"/"+buffer;
    cout << "require " << file.c_str() << endl;
    
    int filefd = open(file.c_str(),O_RDONLY); if(-1 == filefd){
    	perror("open file error");
		return 1;
    }

    struct stat s;
    fstat(filefd,&s);
    sendfile(connfd,filefd,0,s.st_size);
    //5.关闭套接字
    close(connfd);
    close(fd);
}
  • load.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <thread>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//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);

    int pos = s.find_last_of('/');
    //查询是否找到
    if(pos != string::npos){ //找到了
    	s = s.substr(pos);
    }
    string file = "./" + s;
    cout << file << endl;

    int filefd = open(file.c_str(),O_CREAT|O_WRONLY,0666);
    if(-1 == filefd){
    	perror("open file error");
	return 1;
    }
    //下载文件
    for(;;){
    	char buffer[256] = {0};
		int n = read(fd,buffer,256);
		write(filefd,buffer,n);
		if(n < 256) break;
    }

    //4.关闭套接字
    close(filefd);
    close(fd);
}

流程:
开启服务器,开启客户端,输入下载文件名称

[root@localhost 2]# ./file_sever 0.0.0.0 8080 /root/Desktop/2021/C/c_chujie/1
bind
listen
accept
2.c
require /root/Desktop/2021/C/c_chujie/1/2.c
...
[root@localhost 2]# ./load 127.0.0.1 8080
2.c
./2.c
[root@localhost 2]# ll
total 152
-rw-r--r--. 1 root root   193 Jun  4 00:14 2.c
-rwxr-xr-x. 1 root root 28680 Jun  2 01:35 client
-rw-r--r--. 1 root root  1334 Jun  2 01:35 client2.cpp
-rw-r--r--. 1 root root  1242 Jun  1 23:33 client.cpp
-rwxr-xr-x. 1 root root 18568 Jun  4 00:12 file_sever
-rw-r--r--. 1 root root  1788 Jun  4 00:12 file_sever.cpp
-rwxr-xr-x. 1 root root 18336 Jun  3 23:55 load
-rw-r--r--. 1 root root  1582 Jun  3 23:55 load.cpp
-rwxr-xr-x. 1 root root 49608 Jun  2 01:34 sever
-rw-r--r--. 1 root root  2328 Jun  2 01:53 sever2.cpp
-rw-r--r--. 1 root root  1519 Jun  1 23:05 sever.cpp

2.4.5 实例5:多个客户端从服务器多次下载文件

  • file_sever2.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <thread>
#include <vector>
using namespace std;
int main(int argc,char* argv[]) {
    if(4 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port path\n" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

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

        string cmd = string("ls -lR ")+argv[3];
        FILE* pfile = popen(cmd.c_str(),"r");
        if(NULL ==  pfile) {
            perror("popen error");
            return 1;
        }
        for(;;) {
            char info[1025] = {0};
            int n = fread(info,1,1024,pfile);
            write(connfd,info,n);
            if(n < 1024) break;
        }
        pclose(pfile);

        thread t([=]() {
            for(;;) {
                char buffer[256] = {0};
                int n = read(connfd,buffer,256);
                if(n <= 0) break;
                cout << buffer << endl;
                //请求文件的路径
                string file = string(argv[3])+"/"+buffer;
                cout << "require " << file.c_str() << endl;

                int filefd = open(file.c_str(),O_RDONLY);
                if(-1 == filefd) {
                    perror("open file error");
                    return;
                }

                struct stat s;
                fstat(filefd,&s);
                sendfile(connfd,filefd,0,s.st_size);
            }
            close(connfd);
        });
        t.detach();
    }
    close(fd);
}
  • load2.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <thread>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
        return 1;
    }
    //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(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    //2.连接服务器
    int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
    if(-1 == res) {
        perror("connect error");
        return 1;
    }

    for(;;) {
        char buffer[1024] = {0};
        int n = read(fd,buffer,1024);
        write(STDOUT_FILENO,buffer,n);
        //cout << buffer;
        //cout.flush();
        if(n < 1024) break;
    }

    string s;
    while(cin >> s) {
        write(fd,s.c_str(),s.size()+1);

        int pos = s.find_last_of('/');
        //查询是否找到
        if(pos != string::npos) { //找到了
            s = s.substr(pos);
        }
        string file = "./" + s;
        cout << file << endl;

        int filefd = open(file.c_str(),O_CREAT|O_WRONLY,0666);
        if(-1 == filefd) {
            perror("open file error");
            return 1;
        }
        //下载文件
        for(;;) {
            char buffer[256] = {0};
            int n = read(fd,buffer,256);
            write(filefd,buffer,n);
            if(n < 256) break;
        }

        //4.关闭套接字
        close(filefd);
    }
    close(fd);
}

结果分析:打开两个客户端分别从服务器下载文件

[root@localhost 2]# ./file_sever2 0.0.0.0 8080 /root/Desktop/2021/C/Test1
bind
listen
accept
2059.c
require /root/Desktop/2021/C/Test1/2059.c
accept
asx.c
require /root/Desktop/2021/C/Test1/asx.c
open file error: No such file or directory
accept
2093.c
require /root/Desktop/2021/C/Test1/2093.c
...
//客户端1
[root@localhost 2]# ./load2 127.0.0.1 8080
/root/Desktop/2021/C/Test1:
total 80
-rw-r--r--. 1 root root   280 Mar  8  2021 2059.c
-rw-r--r--. 1 root root   383 Mar  7  2021 2093.c
-rw-r--r--. 1 root root   261 Mar  9  2021 2111.c
-rw-r--r--. 1 root root   359 Mar 17  2021 2137.c
-rw-r--r--. 1 root root   195 Mar  4  2021 235.c
-rw-r--r--. 1 root root   152 Mar  8  2021 2405.c
-rw-r--r--. 1 root root   205 Mar  8  2021 2406.c
-rw-r--r--. 1 root root   169 Mar  8  2021 2407.c
-rw-r--r--. 1 root root   300 Mar  9  2021 2408.c
-rw-r--r--. 1 root root   259 Mar  8  2021 2415.c
-rw-r--r--. 1 root root   269 Mar  9  2021 2416.c
-rw-r--r--. 1 root root   229 Mar  8  2021 2417.c
-rw-r--r--. 1 root root   430 Mar  9  2021 2418.c
-rw-r--r--. 1 root root   348 Mar  9  2021 2419.c
-rwxr-xr-x. 1 root root 11136 Mar 17  2021 a.out
-rw-r--r--. 1 root root   177 Mar  4  2021 daoxu.c
-rw-r--r--. 1 root root   322 Mar  4  2021 rili.c
-rw-r--r--. 1 root root   286 Mar  8  2021 zsx.c
2059.c
./2059.c
...
//客户端2
[root@localhost 2]# ./load2 127.0.0.1 8080 
/root/Desktop/2021/C/Test1:
total 80
-rw-r--r--. 1 root root   280 Mar  8  2021 2059.c
-rw-r--r--. 1 root root   383 Mar  7  2021 2093.c
-rw-r--r--. 1 root root   261 Mar  9  2021 2111.c
-rw-r--r--. 1 root root   359 Mar 17  2021 2137.c
-rw-r--r--. 1 root root   195 Mar  4  2021 235.c
-rw-r--r--. 1 root root   152 Mar  8  2021 2405.c
-rw-r--r--. 1 root root   205 Mar  8  2021 2406.c
-rw-r--r--. 1 root root   169 Mar  8  2021 2407.c
-rw-r--r--. 1 root root   300 Mar  9  2021 2408.c
-rw-r--r--. 1 root root   259 Mar  8  2021 2415.c
-rw-r--r--. 1 root root   269 Mar  9  2021 2416.c
-rw-r--r--. 1 root root   229 Mar  8  2021 2417.c
-rw-r--r--. 1 root root   430 Mar  9  2021 2418.c
-rw-r--r--. 1 root root   348 Mar  9  2021 2419.c
-rwxr-xr-x. 1 root root 11136 Mar 17  2021 a.out
-rw-r--r--. 1 root root   177 Mar  4  2021 daoxu.c
-rw-r--r--. 1 root root   322 Mar  4  2021 rili.c
-rw-r--r--. 1 root root   286 Mar  8  2021 zsx.c
2093.c
./2093.c
^C
[root@localhost 2]# ll
total 244
-rw-r--r--. 1 root root   280 Jun  4 01:40 2059.c
-rw-r--r--. 1 root root   383 Jun  4 01:41 2093.c
-rw-r--r--. 1 root root     0 Jun  4 01:41 asx.c
-rwxr-xr-x. 1 root root 28680 Jun  2 01:35 client
-rw-r--r--. 1 root root  1334 Jun  2 01:35 client2.cpp
-rw-r--r--. 1 root root  1242 Jun  1 23:33 client.cpp
-rwxr-xr-x. 1 root root 34104 Jun  4 01:03 file_sever
-rwxr-xr-x. 1 root root 34104 Jun  4 01:39 file_sever2
-rw-r--r--. 1 root root  2527 Jun  4 01:38 file_sever2.cpp
-rw-r--r--. 1 root root  2365 Jun  4 01:19 file_sever2.cpp.orig
-rw-r--r--. 1 root root  1788 Jun  4 00:46 file_sever.cpp
-rwxr-xr-x. 1 root root 18336 Jun  3 23:55 load
-rwxr-xr-x. 1 root root 18432 Jun  4 01:11 load2
-rw-r--r--. 1 root root  1908 Jun  4 01:36 load2.cpp
-rw-r--r--. 1 root root  1752 Jun  4 01:11 load2.cpp.orig
-rw-r--r--. 1 root root  1582 Jun  4 00:25 load.cpp
-rw-r--r--. 1 root root     0 Jun  4 01:19 s
-rwxr-xr-x. 1 root root 49608 Jun  2 01:34 sever
-rw-r--r--. 1 root root  2328 Jun  2 01:53 sever2.cpp
-rw-r--r--. 1 root root  1519 Jun  1 23:05 sever.cpp

2.5 UDP基本流程

在这里插入图片描述

2.5.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); 

实例1:采用UDP实现单向发送信息

  • send.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "send_ip send_port" << endl;
        return 1;
    }
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == fd) {
        perror("open socket error");
        return 1;
    }
    //编辑套接字信息
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    string s;
    getline(cin,s);
    sendto(fd,s.c_str(),s.size()+1,0,(sockaddr*)&addr,sizeof(addr));

    close(fd);
}
  • recv.cpp
include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "recv_ip recv_port" << endl;
        return 1;
    }
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == fd) {
        perror("open socket error");
        return 1;
    }
    //编辑套接字信息
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    bind(fd,(sockaddr*)&addr,sizeof(addr));
    char buffer[1024] = {0};
    recv(fd,buffer,1023,0);
    cout << buffer << endl;
    close(fd);
}
[root@localhost udp]# ./send 0.0.0.0 8080
123
...
[root@localhost udp]# ./recv 127.0.0.1 8080
123

实例2:采用UDP实现双向发送信息

  • send2.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "send_ip send_port" << endl;
        return 1;
    }
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == fd) {
        perror("open socket error");
        return 1;
    }
    //编辑套接字信息
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    string s;
    getline(cin,s);
    sendto(fd,s.c_str(),s.size()+1,0,(sockaddr*)&addr,sizeof(addr));

    char buffer[1024] = {0};
    recv(fd,buffer,1023,0);
    cout << buffer << endl;
    close(fd);
}
  • recv2.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "recv_ip recv_port" << endl;
        return 1;
    }
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == fd) {
        perror("open socket error");
        return 1;
    }
    //编辑套接字信息
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    bind(fd,(sockaddr*)&addr,sizeof(addr));
    char buffer[1024] = {0};
    //recv(fd,buffer,1023,0);
    //cout << buffer << endl;

    sockaddr_in remote_addr;
    socklen_t len = sizeof(remote_addr);
    recvfrom(fd,buffer,1023,0,(sockaddr*)&remote_addr,&len);
    cout << buffer << endl;

    string s;
    getline(cin,s);
    sendto(fd,s.c_str(),s.size()+1,0,(sockaddr*)&remote_addr,len);

    close(fd);
}
[root@localhost udp]# ./recv2 0.0.0.0 8080
123
abc
...
[root@localhost udp]# ./send2 127.0.0.1 8080
123 
abc

2.5.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);

实例:多播

  • send.cpp
    同上
  • multirecv.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(4 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "recv_ip recv_port multi_ip" << endl;
        return 1;
    }
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == fd) {
        perror("open socket error");
        return 1;
    }
    //编辑套接字信息
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    bind(fd,(sockaddr*)&addr,sizeof(addr));

    struct ip_mreq group;
    group.imr_multiaddr.s_addr = inet_addr(argv[3]);
    group.imr_interface.s_addr = inet_addr(argv[1]);
    if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0){
        perror("Adding multicast group error");
        close(fd);
        return 1;
    }
    
    char buffer[1024] = {0};
    recv(fd,buffer,1023,0);
    cout << buffer << endl;
    close(fd);
}

2.5.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); 
  • 接收者

与单播发送者一致

实例:广播

  • boardsend.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc,char* argv[]) {
    if(3 != argc) {
        cout << "argument error" << endl;
        cout << "Usage:" << argv[0] << "send_ip send_port" << endl;
        return 1;
    }
    //1.打开套接字
    int fd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == fd) {
        perror("open socket error");
        return 1;
    }
    int opt  = 1;
    if(setsockopt(fd,SOL_SOCKET,SO_BROADCAST,(char*)&opt,sizeof(opt))==-1) {
        perror("setsockopt fail.");
        close(fd);
        return 1;  
    }
    //编辑套接字信息
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton(argv[1],&addr.sin_addr); //点分十进制数串转网络字节序长整型,存入addr的IP地址
    addr.sin_port = htons(atoi(argv[2]));//addr的端口号 = 主机序转化为网络序

    string s;
    getline(cin,s);
    sendto(fd,s.c_str(),s.size()+1,0,(sockaddr*)&addr,sizeof(addr));

    close(fd);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二、Linux网络编程:Socket编程-接口 的相关文章

  • Linq - 从表达式 创建表达式

    我有一个谓词Expression
  • 使用 Json.NET 序列化子类

    我正在尝试使用 Json NET 序列化子类 生成的 json 包含超类的序列化属性 但是not子类对象的属性 这似乎与我发现的一个问题有关这里就这样 https stackoverflow com q 5863496 498969 但必须
  • 自动映射器多对多 stackoverflowException

    我遇到以下映射的堆栈溢出 Mapper CreateMap
  • 嵌入资源文件的路径

    我的资源文件中有一个图标 我想引用它 这是需要图标文件路径的代码 IWshRuntimeLibrary IWshShortcut MyShortcut MyShortcut IWshRuntimeLibrary IWshShortcut W
  • 无法加载程序集问题

    我收到以下错误 无法加载程序集 错误详细信息 System BadImageFormatException 无法加载文件或程序集 文件 或其依赖项之一 该程序集是由比当前加载的运行时更新的运行时构建的 无法加载 该程序集是使用 Net Fr
  • opencv中如何去除二值图像噪声?

    将图像转换为二值图像 黑白 后如果有任何噪音怎么办 我消除了那些不需要的噪音 您可以看到下图的黑色区域内有一些白噪声 我该如何去除噪声 使用opencv http img857 imageshack us img857 999 blackn
  • Visual Studio 中列表框的上移、下移按钮[重复]

    这个问题在这里已经有答案了 我正在尝试制作一个上移按钮和一个下移按钮 以移动 Microsoft Visual Studio 2012 中列表框中的选定项目 我已经在 WDF jquery winforms 和其他一些表单中看到了其他示例
  • 对作为函数参数传递的指针使用删除

    删除作为函数参数传递的指针是否可以 并且合法 如下所示 include
  • 如何从外语线程调用Python函数(C++)

    我正在开发一个程序 使用 DirectShow 来抓取音频数据 媒体文件 DirectShow 使用线程将音频数据传递给回调 我的程序中的函数 然后我让该回调函数调用另一个函数 Python 中的函数 我使用 Boost Python 来包
  • 配置:错误:无法运行C编译的程序

    我正在尝试使用 Debian Wheezy 操作系统在我的 Raspberry Pi 上安装不同的软件 当我运行尝试配置软件时 我尝试安装我得到此输出 checking for C compiler default output file
  • 当需要不同数量和类型的参数时如何创建操作委托列表

    我们有一组大约两打的类 它们继承自具有抽象 Validate 方法的基类 当然 每个类都有不同的验证需求 但它们之间的不同组合需要规则 因此 正如您可以想象的那样 这导致了大量代码重复 例如 A 类需要规则 1 3 6 和 9B 类需要规则
  • 如何检测应用程序正在运行的 .NET 版本?

    我尝试使用Environment Version ToString 确定目标计算机上正在使用什么 NET 框架 但安装了 4 0 版本时 它说我正在使用 NET 2 0 如何检测目标计算机上正在运行的 NET Framework 版本 En
  • 如何将System.Windows dll添加到Visual Studio 2010 Express?

    我正在开发一个小型应用程序C and VS2010 as IDE with NET框架4 我想用CaptureSource类以便从笔记本电脑的网络摄像头捕获视频 为此我需要添加一个命名空间System Windows DependencyO
  • “1个未解决的外部”C++

    我已经检查了所有文件之间的连接以及类和函数定义 但每次我尝试运行我的程序时 它都会阻止我并告诉我它有 1 个未解析的外部 该程序应该打开多个文件 一个 学生 文件和一个 成绩 文件 从中读取数据 然后使用 查询文件 来查找数据 找到查询中要
  • 使用多态对象数组进行 JSON 反序列化

    我在涉及多态对象数组的 JSON 反序列化方面遇到问题 我已经尝试过记录的序列化解决方案here https stackoverflow com questions 5186973 json serialization of array w
  • 具有四个 && 的 LINQ Where 子句

    我正在尝试在Where 子句中创建一个带有4 个参数的LINQ 查询 这是一个 Windows 8 应用程序项目 我正在使用 SQLite 数据库 SQLite 实现 https github com praeclarum sqlite n
  • 为什么在构造函数中设置字段是(或不是)线程安全的?

    假设您有一个像这样的简单类 class MyClass private readonly int a private int b public MyClass int a int b this a a this b b public int
  • 如何在 C 中创建最低有效位设置为 1 的掩码

    这个功能如何运作 最低有效 n 位设置为 1 的掩码 Example n 6 gt 0x2F n 17 gt 0x1FFFF 我根本不明白这些 尤其是 n 6 gt 0x2F 另外 什么是面膜 通常的方法是采取1 并将其左移n位 这会给你类
  • 实体框架代码首次日期字段创建

    我正在使用实体框架代码优先方法来创建我的数据库表 下面的代码 创建一个DATETIME数据库中的列 但我想创建一个DATE柱子 DataType DataType Date DisplayFormatAttribute ApplyForma
  • 如何使用 C# 为 azure devops 变量赋值

    我有 selenium C 测试脚本 可以从浏览器获取令牌 我有两个 azure devops 任务 一个用于执行 selenium 测试 另一个用于执行 API 测试 我想将 selenium 测试获取的令牌传递给 API 测试执行任务

随机推荐

  • 最全Mac&Win软件分享

    由于诸多因素影响 无法再分享相关的资料 如果无法访问GitHub的话大家可以去搜一下 GitHub加速 直接搜索找到相关的解决方案即可 包含常用的所有软件以及在线工具等等 GitHub地址 other doc Tools at main c
  • 微信小程序审核需要多久?微信小程序审核时间加快至2小时!

    8月15日起 微信将上线小程序全新审核机制 为第三方服务商的代码提审铺设 快车道 以往 小程序审核更像是 单车道 同一个第三方 同一时间审核大批量的小程序 也只能一一排队等候通过 8月15日起 平台将上线第三方预检加速机制 同一时间大批量提
  • PYTHON 编写 识别图片中两个峰值的代码

    Python 编写用于识别图片中的两个峰值的代码的方法有很多种 主要可以使用 OpenCV 和 NumPy 等库来实现 具体的代码可以参考网上的一些文章 例如 https www geeksforgeeks org python detec
  • Linux高性能服务器编程 学习笔记 第二章 IP协议详解

    本章从两方面探讨IP协议 1 IP头部信息 IP头部出现在每个IP数据报中 用于指定IP通信的源端IP地址 目的端IP地址 指导IP分片和重组 指定部分通信行为 2 IP数据报的路由和转发 IP数据报的路由和转发发生在除目标机器外的所有主机
  • msvcp140.dll重新安装的解决方法

    在打开游戏或者软件的时候 电脑提示msvcp140 dll丢失无法运行需要怎么办 相信这个问题困扰着不少小伙伴 msvcp140 dll是Windows系统中非常重要的动态连接组件 是连接程序与系统的必不可少的文件 小编今天就把重新安装的解
  • Java中long的表达式问题

    今天在代码里发现了有个抛错 是由下面这段分片上传时定位的代码捕获的 第一想法是是不是由于包太大 6 4G 导致long的offset超限 虽然long好像没有这么短 然后查了下long的最大值Long MAX VALUE 2的63次方 1
  • python监听端口获取数据_python从网络端口读取文本数据

    python从网络端口读取文本数据 To test it with netcat start the script and execute echo Hello cat ncat exe 127 0 0 1 12345 import soc
  • Pytorch中实现CPU和GPU之间的切换

    如何在pytorch中指定CPU和GPU进行训练 以及cpu和gpu之间切换 由CPU切换到GPU 要修改的几个地方 网络模型 损失函数 数据 输入 标注 创建网络模型 tudui Tudui if torch cuda is availa
  • 递归、加法原理,如何分解问题(独立且完备的划分)

    加法原理适用于做一件事有n种独立不相交且完备的方向 每个方向上有ai种方案 则总的方案数就是 a1 a2 an 例题 把n个数分为k个非空子集 有多少种分法 分解问题 第一个集合里放多少个数把原问题的解分成了独立且完备的若干方向 分别解每个
  • 如何基于数据分析精准定位你的用户群?

    还没关注 快动动手指 01 行为事件分析 行为事件分析 对于很多业务人员来说相对比较陌生 但它却是用户分析的第一步 也是用户分析的核心和基础 一般来说事件通过埋点来获得 行为事件分析法主要用于研究某行为事件的发生对企业组织价值的影响以及影响
  • JAVA_import导入类

    如果我们要使用其他包的类 需要使用import导入 从而可以在本类中直接通过类名来调用 否则就需要书写类的完整包名和类名 import后 便于编写代码 提高可维护性 注意要点 1 Java会默认导入java lang包下所有的类 因此这些类
  • STM32CUBEIDE 环境下printf()和scanf()运行失败的原因

    STM32CUBEIDE 环境下printf 和scanf 运行失败的原因 问题重现 int io putchar int ch HAL UART Transmit huart1 uint8 t ch 1 0xFFFF
  • 解决Centos虚拟机复制文件失败问题

    问题 Error when getting information for file tmp VMwareDnD p6v6B6 No such file or directory 解决 安装 卸载预装的 open vm tools 包 检测
  • Google hacking了解

    google hacking方法技术手册 1 黄金法则 不区分大小写 通配符只表示词组中一个词 32个单词限制 关键字 双引号强制搜索包含关键字内容 intext password username userid xxx 1 利用搜索引擎进
  • 【测试】利用LoadComplete确定不同负载下的服务器性能

    LoadComplete是一个负载测试工具 用于为Web服务器和服务创建和运行自动负载测试 它可以在手机 网站和web应用程序上设计和运行性能 压力和负载测试 您不需要有编程知识 创建一个负载测试只需要几秒钟的时间 接下来我们通过LoadC
  • ELK系列(二)、在Kibana中使用RESTful操作ES库

    上一篇讲了如何安装ELK ELK系列 一 安装ElasticSearch Logstash Kibana Filebeat v7 7 0 这篇介绍如何使用kibana连接ES并操作 先介绍一下ES和关系型数据库的概念对应 在ES7以前的版本
  • 字典树p8036

    Description 给定 n 个模式串 1 2 s1 s2 sn 和 q 次询问 每次询问给定一个文本串 ti 请回答 1 s1 sn 中有多少个字符串 sj 满足 ti 是 sj 的前缀 一个字符串 t 是 s 的前缀当且仅当从 s
  • Windows下MySQL的详细安装教程

    1 安装之前需要注意的几点 建议不要安装最新版本 一般找mysql5 0系列版本即可 mysq1官网有 zip和 msi两种安装形式 zip是压缩包 直接解压缩以后使用的 需要自己配置各种东西 msi是安装包 系统直接帮我们安装搞定 新手建
  • (c)面向过程与(c++)面向对象有什么区别

    相信大家都有一定的了解 c语言是一个面向过程的语言 而c 是一个面向对象的语言 那么面向对象和面向过程有什么区别呢 各举一个例子吧 C面向过程 gt 从面向过程的方向考虑就像我要开车我就要先开门 系安全带 打火 松手刹 挂挡 起步 我们关心
  • 二、Linux网络编程:Socket编程-接口

    2 Socket编程 接口 2 1 接口转换 转接口的换操作主要分为三类 字节序转换操作 IP地址转换操作和主机名转换操作 2 1 1 字节序转换操作 网络序转主机序 函数 含义 作用 ntohs network to host short