Linux Socket编程接口及流程整理

一、结构体

结构体 功能 特性
struct sockaddr 套接字地址结构 IPv4 / IPv6
struct sockaddr_in IPv4套接字地址结构 IPv4
struct in_addr 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];	// 填充字节,用来保证struct sockaddr_in的大小和struct sockaddr的大小相等
};

// IPv4 地址类型
typedef unsigned int in_addr_t;

// IPv4 地址结构
struct in_addr
{
    in_addr_t s_addr;
};

// IPv6 套接字地址结构
struct sockaddr_in6
{
    uint8_t sin6_len;		// 固定为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地址
};

二、转换接口

头文件:arpa/inet.h

1、字节序转换

  • 网络序转主机序
函数 作用
ntohs() unsigned short类型从网络序转换到主机序
ntohl() unsigned long类型从网络序转换到主机序
  • 主机序转网络序
函数 作用
htons() unsigned short类型从主机序转换到网络序
htonl() unsigned long类型从主机序转换到网络序

实例:

#include <iostream>
#include <cstring>
#include <iomanip>
#include <arpa/inet.h>

void Hex(int n)
{
    char bytes[2];
    memcpy(bytes, &n, 2);
    std::cout << showbase << setw(2) << setfill('0') 
        << hex << (int)bytes[0] << setw(2) << (int)bytes[1] << std::endl;
}

int main()
{
    uint16_t n = 16;
    Hex(n);
    
    uint16_t m = htons(n);
    Hex(m);
    
    uint16_t t = ntohs(m);
    Hex(t);
    
    return 0;
}

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>

int main()
{
    std::string s = "192.168.0.1";
    struct in_addr addr;
    inet_aton(s.c_str(), &addr);
    std::cout << hex << addr.s_addr << "\n" 
        << inet_ntoa(addr) << "\n"
        << inet_addr(s.c_str()) << std::endl;

    return 0;
}

3、主机名转换

函数 功能
struct hostent* gethostbyname(const char *hostname) 主机名转地址
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地址的列表

三、Socket操作

头文件:sys/socket.h

1、创建

/**
 * 参数
 * 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 表示失败, 否则返回 socket 描述符 (>0)
 */
int socket(int domain, int type, int protocol);

2、关闭

/**
 * 参数
 * sockfd: socket 描述符
 * howto: 关闭方式 - SHUT_RD (值为0, 关闭连接的读); SHUT_WR (值为1, 关闭连接的写); SHUT_RDWR (值为2, 连接读写都关闭)
 *
 * 返回值
 * 成功返回 0, 失败返回 -1
 */
int close(int sockfd);
int shutdown(int sockfd, int howto);

// 区别
// 1、close 把描述符的引用计数减 1,仅在该计数变为 0 时关闭套接字。shutdown 可以不管引用计数就激发 TCP 的正常连接终止序列
// 2、close 终止读和写两个方向的数据发送。TCP 为全双工,有时候需要告知对方已经完成了数据传送,即使对方仍有数据要发送

3、属性

/**
 * 参数
 * sockfd: socket 描述符
 * level: 选项层次 - SOL_SOCKET (通用套接字选项); IPPROTO_TCP (TCP选项); IPPROTO_IP (IP选项); IPPROTO_IPV6 (IPv6选项)
 * optname: 选项
 * optval: 选项值指针
 * optlen: optval缓冲区长度
 *
 * 返回值
 * 成功返回 0, 失败返回 -1
 */
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

4、获取

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

5、绑定

/**
 * 参数
 * socket: 套接字描述符
 * address: 地址和端口号
 * address_len: address 缓冲区长度
 *
 * 返回值
 * 成功返回 0, 失败返回 SOCKET_ERROR
 */
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

6、监听

/**
 * 参数
 * sockfd: 监听的 socket 描述符
 * backlog: 排队的最大连接个数
 *
 * 返回值
 * 成功返回 0, 失败返回 -1
 */
int listen(int sockfd, int backlog);

7、连接

/**
 * 参数
 * sockfd: 客户端 socket 描述符
 * addr: 服务器的 socket 地址
 * addrlen: 服务器的 socket 地址长度
 *
 * 返回值
 * 成功返回 0, 失败返回 -1
 */
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

8、接收连接

/**
 * 参数
 * sockfd: 服务器 socket 描述符, 监听 socket 描述符
 * addr: 客户端 socket 地址
 * addrlen: 客户端 socket 地址长度
 *
 * 返回值
 * 失败返回 -1, 否则返回连接描述符
 */
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

说明
如果不需要获取客户端套接字地址,addraddrlen传入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);

9、发送数据

  • 方法 1
/**
 * 参数
 * fd: 文件描述符
 * buf: 写入数据
 * len: 写入数据的长度
 *
 * 返回值
 * 出错 <0, 否则返回实际所写字节数
 */
ssize_t write(int fd, const void *buf, size_t len);
  • 方法 2
ssize_t send(int sockfd, const void *buf, size_t len, int flags); // flags 通常为 0
  • 方法 3
/**
 * 参数
 * sockfd: socket 描述符
 * buf: 写入数据
 * len: 写入数据长度
 * flags: 通常为 0
 * dst_addr: 目标 socket 地址
 * addrlen: 目标 socket 地址长度
 *
 * 返回值
 * 出错 <0, 否则返回实际所写字节数
 */
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dst_addr, socklen_t addrlen);

10、接收数据

  • 方法 1
// 返回值:
//  0 - 读到文件结束
// >0 - 实际所写字节数
// <0 - 出错
ssize_t read(int fd, void *buf, size_t len);
  • 方法 2
ssize_t recv(int sockfd, void *buf, size_t len, int flags);	// flags 通常为 0
  • 方法 3
/**
 * 参数
 * sockfd: socket 描述符
 * buf: 读取数据
 * len: 读取长度
 * flags: 通常为 0
 * src_addr: 源端 socket 地址
 * addrlen: 源端 socket 地址长度
 */
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

四、TCP

image.png

1、客户端伪码

main()
{
    // 1、打开套接字
    fd = socket();
    
    // 2、连接服务器
    connect(fd);
    
    // 3、写入/读取数据
    write(fd);
    read(fd);
    
    // 4、关闭套接字
    close(fd);
}

2、服务端伪码

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);
}

3、TCP 实例

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

// Client
int Client()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == fd)
    {
        perror("open socket");
        return -1;
    }
    
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton("127.0.0.1", &addr.sin_addr);
    addr.sin_port = htons(8080);
    
    if(-1 == connect(fd, (struct sockaddr*)&addr, sizeof(addr)))
    {
        perror("connect");
        return -1;
    }
    
    std::string s;
    std::cin >> s;
    write(fd, s.c_str(), s.size() + 1);
    
    char buf[256] = {0};
    read(fd, buf, 256);
    std::cout << buf << std::endl;
    
    close(fd);
    
    return 0;
}

// Server
int Server()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == fd)
    {
        perror("open socket");
        return -1;
    }
    
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    inet_aton("127.0.0.1", &addr.sin_addr);
    addr.sin_port = htons(8080);
    
    if(-1 == bind(fd, (struct sockaddr*)&addr, sizeof(addr)))
    {
        perror("bind");
        return -1;
    }
    
    if(-1 == listen(fd, 4))
    {
        perror("listen");
        return -1;
    }
    
    int connfd = accept(fd, NULL, NULL);
    if(-1 == connfd)
    {
        perror("accept");
        return -1;
    }
    
    char buf[256] = {0};
    read(connfd, buf, 256);
    std::cout << buf << std::endl;
    
    std::string s;
    std::cin >> s;
    write(connfd, s.c_str(), s.size() + 1);
    
    close(connfd);
    close(fd);
    
    return 0;
}

4、父子进程实例

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

using namespace std;

// Client
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("connect");
		return 1;
	}

	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;
		}
	}

	close(fd);

	return 0;
}

// Server
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("bind");
		return 1;
	}

	res = listen(fd, 4);
	if (-1 == res)
	{
		perror("listen");
		return 1;
	}

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

	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);
		}
	}

	close(connfd);
	close(fd);

	return 0;
}

5、C++ 多线程实例

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <thread>

using namespace std;

// Client
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("connect");
		return 1;
	}

	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);
	}

	close(fd);

	return 0;
}

// Server
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("bind");
		return 1;
	}

	res = listen(fd, 4);
	if (-1 == res)
	{
		perror("listen");
		return 1;
	}

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

	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);
	}

	close(connfd);
	close(fd);

	return 0;
}

6、多客户端实例

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <thread>
#include <vector>

using namespace std;

// Server
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("bind");
		return 1;
	}
	cout << "bind" << endl;

	res = listen(fd, 4);
	if (-1 == res)
	{
		perror("listen");
		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 (;;)
	{
		struct sockaddr_in remote_addr;
		socklen_t len = sizeof(remote_addr);
		int connfd = accept(fd, (sockaddr *)&remote_addr, &len);

		// 打印哪个客户端连接服务器
		cout << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;

		if (-1 == connfd)
		{
			perror("accept");
			return 1;
		}
		fds.push_back(connfd);
		cout << "accept" << endl;

		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();
	}

	for (auto connfd : fds)
	{
		close(connfd);
	}
	close(fd);

	return 0;
}

7、文件服务器实例

#include <iostream>
#include <string>
#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;

// Client
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("connect");
		return 1;
	}

	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");
		return 1;
	}

	//下载文件
	for (;;)
	{
		char buffer[256] = {0};
		int n = read(fd, buffer, 256);
		write(filefd, buffer, n);
		if (n < 256)
		{
			break;
		}
	}

	close(filefd);
	close(fd);

	return 0;
}

// Server
int main(int argc, char *argv[])
{
	if (4 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port path" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("bind");
		return 1;
	}

	res = listen(fd, 4);
	if (-1 == res)
	{
		perror("listen");
		return 1;
	}

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

	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");
		return 1;
	}

	struct stat s;
	fstat(filefd, &s);
	sendfile(connfd, filefd, 0, s.st_size);

	close(connfd);
	close(fd);

	return 0;
}

8、多客户端文件服务

#include <iostream>
#include <string>
#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;

// Client
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("connect");
		return 1;
	}

	for (;;)
	{
		char buffer[1024] = {0};
		int n = read(fd, buffer, 1024);
		write(STDOUT_FILENO, buffer, n);
		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");
			return 1;
		}

		//下载文件
		for (;;)
		{
			char buffer[256] = {0};
			int n = read(fd, buffer, 256);
			write(filefd, buffer, n);
			if (n < 256)
			{
				break;
			}
		}
		close(filefd);
	}
	close(fd);

	return 0;
}

// Server
int main(int argc, char *argv[])
{
	if (4 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "sever_ip sever_port path" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	int res = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == res)
	{
		perror("bind");
		return 1;
	}

	res = listen(fd, 4);
	if (-1 == res)
	{
		perror("listen");
		return 1;
	}

	for (;;)
	{
		int connfd = accept(fd, NULL, NULL);
		if (-1 == connfd)
		{
			perror("accept");
			return 1;
		}

		string cmd = string("ls -lR ") + argv[3];
		FILE *pfile = popen(cmd.c_str(), "r");
		if (NULL == pfile)
		{
			perror("popen");
			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");
						return;
					}

					struct stat s;
					fstat(filefd, &s);
					sendfile(connfd, filefd, 0, s.st_size);
				}
				close(connfd);
			}
		);
		t.detach();
	}
	close(fd);
	
	return 0;
}

五、UDP

image.png

1、单播

  • 伪码
// Client
main()
{
    // 打开 socket
    connfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    // 设置IP和端口
    struct sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr.s_addr = inet_addr(ip);
    
    // 发送数据
    sendto(connfd, buf, buf_size, 0, (struct sockadrr *)&si, sizeof(si));
    
    // 关闭 socket
    close(connfd);
}

// Server
main()
{
    // 打开 socket
    connfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    // 设置IP和端口
    struct sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr.s_addr = INADDR_ANY;	// 接收任意IP、任意网卡发给指定端口的数据
    
    // 绑定端口
    bind(connfd, (struct socket *)&si, sizeof(si));
    
    // 接收数据
    recv(connfd, buf, buf_size, 0);
    
    // 关闭 socket
    close(connfd);
}
  • 单向发送实例
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

using namespace std;

// Client
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "send_ip send_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

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

	close(fd);

	return 0;
}

// Server
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << "\n";
		cout << "Usage:" << argv[0] << "recv_ip recv_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

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

	char buffer[1024] = {0};
	recv(fd, buffer, 1023, 0);
	cout << buffer << endl;

	close(fd);

	return 0;
}
  • 双向发送实例
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

using namespace std;

// Client
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << endl;
		cout << "Usage:" << argv[0] << "send_ip send_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

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

	char buffer[1024] = {0};
	recv(fd, buffer, 1023, 0);
	cout << buffer << endl;

	close(fd);

	return 0;
}

// Server
int main(int argc, char *argv[])
{
	if (3 != argc)
	{
		cout << "argument error" << endl;
		cout << "Usage:" << argv[0] << "recv_ip recv_port" << endl;
		return 1;
	}

	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	char buffer[1024] = {0};

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

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

	close(fd);

	return 0;
}

2、多播 / 组播

  • 伪码
// 客户端流程与单播一致

// Server
main()
{
    // 打开套接字
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    
    // 构建服务器地址结构
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);
    
    // 绑定
    bind(fd, server_addr, sizeof(server_addr));
    
    // 构建多播属性结构
    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");		// 设置网卡接口
    
    // 设置多播权限和属性
    setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group));
    
    // 设置客户端多播地址
    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);
    
    // 发送数据
    sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
    
    // 关闭套接字
    close(fd);
}
  • 多播 / 组播 Server 端编码
#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;
	}

	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

	bind(fd, (struct 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");
		close(fd);
		return 1;
	}

	char buffer[1024] = {0};
	recv(fd, buffer, 1023, 0);
	cout << buffer << endl;

	close(fd);

	return 0;
}

3、广播

  • 伪码
// 服务端流程与单播一致

// Client
main()
{
    // 打开 socket
    cfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    // 打开广播
    setsockopt(cfd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n));	// 参数 n - 0 表示关闭, 非 0 表示打开
    
    // 设置IP和端口
    struct sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr.s_addr = inet_addr("255.255.255.255");
    
    // 发送数据
    sendto(cfd, buf, buf_size, 0, (struct sockaddr *)&si, sizeof(si));
    
    // 关闭 socket
    close(socket);
}
  • 客户端编码
#include <iostream>
#include <string>
#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;
	}

	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == fd)
	{
		perror("open socket");
		return 1;
	}

	int opt = 1;
	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt)) == -1)
	{
		perror("setsockopt");
		close(fd);
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	inet_aton(argv[1], &addr.sin_addr);
	addr.sin_port = htons(atoi(argv[2]));

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

	close(fd);

	return 0;
}
posted @ 2022-08-11 11:26  HOracle  阅读(404)  评论(0编辑  收藏  举报