一、函数说明
- 地址接口
1、通用地址接口
struct sockaddr
{
u_short sa_family; //地址类型,IPV4,用宏AG_INET即可;2字节;
char sa_data[14]; //14字节的地址数据;
};
共16字节 = 2字节地址类型 + 14字节地址数据
2、自定义地址接口
struct sockaddr_in
{
short int sin_family; //地址族,IPv4,用宏AF_INET;
unsigned short int sin_port; //端口号,需要htons函数进行字节序转换;
struct in_addr sin_addr; //IP地址,需要inet_addr函数进行转换(点分字符串→数值),该成员本身也是一个结构体;
unsigned char sin_zero[8]; //8个字节填0;
};
- 地址转换
1、需要将点分字符串ip转化为程序ip,使用inet_addr函数:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//inet_addr需要包含以上三个头文件;
struct sockaddr_in servaddr; //先定义一个该种地址接口的结构体变量;
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
2、字节序转换
地址接口配置中的端口需要字节序转换,网络规定使用大端字节序。
#include <arpa/inet.h> //头文件;
//从主机(h)发送到网络(n):
// 把unsigned long类型从主机序转换到网络序
uint32_t htonl(uint32_t hostlong);
// 把unsigned short类型从主机序转换到网络序
uint16_t htons(uint16_t hostshort); //最常用!
//从网络(n)接收到主机(h):
// 把unsigned long类型从网络序转换到主机序
unit32_t ntohl(uint32_t netlong);
// 把unsigned short类型从网络序转换到主机序
unit16_t ntohs(uint16_t netshort);
- 地址接口配置
1、socket:创建套接字
#include<sys/types.h>
#include<sys/socket.h>
int socket(int family, int type, int protocol);
参数 | 含义 |
---|---|
family | 协议族,对于IPV4用宏AF_INET; |
type | 套接字类型:①对于tcp协议用宏SOCK_STREAM;②对于udp协议,用宏SOCK_DGRAM; |
protocol | 指定为0即可; |
返回值 | 成功:返回文件描述符;失败:-1; |
2、bind:绑定
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen);
参数 | 含义 |
---|---|
sockfd | 前面socket创建成功返回的套接字,即文件描述符; |
servaddr | 配置好的通用地址接口.配置好的地址接口,属于struct sockaddr_in 类型,需要转换成第一种通用地址接口;注意是socklen_t类型,可以提前定义一个socklen_t类型变量,然后让其等于sizeof(servaddr) |
addrlen | 配合第二个参数共同确认地址接口变量的内容; |
返回值 | 成功:0;失败:-1; |
3、地址接口初始化
bzero(&servaddr, sizeof(servaddr)); //将地址接口结构体清空;
servaddr.sin_family = AF_INET; //用宏AF_INET代表IPV4;
servaddr.sin_port = htons(8080); //设置端口号(字节序转换);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //这个宏表示任意IP地址,因为服务器不限定来访的客户端IP;
4、listen:监听(服务端专用)
#include<sys/types.h>
#include<sys/socket.h>
int listen(int sockfd, int backlog);
参数 | 含义 |
---|---|
sockfd | socket创建套接字的返回值 |
backlog | 服务器的监听长度,设置1024即可 |
返回值 | 成功:0;失败:-1 |
5、accept:服务端连接
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr* cliaddr, socklen_t * addrlen);
参数 | 含义 |
---|---|
sockfd | socket创建套接字的返回值 |
cliaddr | 是一个输出参数,从accept带回来的的地址接口(客户端),属于struct sockaddr_in 类型,需要转换成第一种通用地址接口 |
addrlen | 配合第二个参数共同确认地址接口变量的内容,取该变量的地址 |
返回值 | 成功:0;失败:-1 |
6、connect:客户端连接
#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd, struct sockaddr* servaddr, socklen_t addrlen);
参数 | 含义 |
---|---|
sockfd | socket创建套接字的返回值 |
servaddr | 配置好的地址接口,属于struct sockaddr_in 类型,需要转换成第一种通用地址接口 |
addrlen | 配合第二个参数共同确认地址接口变量的内容,注意不是地址,直接是该变量 |
返回值 | 成功:0;失败:-1 |
- 收发数据
1、read/write
因为socket返回的是文件描述符,因此也可以像文件一样使用read/write函数进行数据传输。
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int write(int connfd, void* buff, size_t length);
int read(int connfd, void* buff, size_t length);
参数 | 含义 |
---|---|
sockfd | socket创建套接字的返回值 |
buff | 文件缓冲区,一般使用数组 |
length | 想要读(收)、写(发)的字节数,对于单独数组:strlen(buff),对于结构体数组:sizeof(node) |
返回值 | 成功:>0,实际发或者收到的字节数;0:遇到文件尾巴;失败:-1 |
2、send/recv
#include<sys/types.h>
#include<sys/socket.h>
size_t send(int connfd, void* buff, size_t length, int flags);
size_t recv(int connfd, void* buff, size_t length, int flags);
flags功能选项:
① 0:等同于write、read;
② MSG_DONTROUTE:send用,表示不查询路由表,在内部局域网进行通信;
③ MSG_OOB:接收或发送带外数据(插队);
④ MSG_PEEK:read用,表示读取时不删除数据;
⑤ MSG_WAITALL:等待所有数据(阻塞等待);
返回值 :
成功:>0,实际发或者收到的字节数;
0:对方已经下线;
失败:-1;
二、示例代码
客户端client.cpp:文章来源:https://www.toymoban.com/news/detail-829363.html
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
#define SIZE 1024
void connectToServer(int &serverfd){
char sendBuf[SIZE], recvBuf[SIZE];
ssize_t sendRet, recvRet;
while(1){
// 发送数据及接收响应
cout << ">> ";
cin >> sendBuf;
while(sendBuf){
sendRet = send(serverfd, sendBuf, sizeof(sendBuf), 0);
if(strcmp(sendBuf, "$") == 0)
break;
if (sendRet == -1)
cout << "Error sending data\n";
else
cout << "Sent data: " << sendBuf << endl;
cout << ">> ";
cin >> sendBuf;
}
// 接收响应及数据
recvRet = recv(serverfd, recvBuf, sizeof(recvBuf), 0);
while(recvRet > 0){
recvBuf[recvRet] = '\0';
if(strcmp(recvBuf, "$") == 0)
break;
cout << "Received data: " << recvBuf << endl;
recvRet = recv(serverfd, recvBuf, sizeof(recvBuf), 0);
}
// 是否关闭连接
cout << "Do you want to close connection? (y/n)";
char c;
while ((c = getchar()) != '\n' && c != EOF);
cin >> sendBuf;
while(strcmp(sendBuf, "y") !=0 && strcmp(sendBuf, "n") != 0){
cout << "Wrong input! Please input a letter 'y' or 'n' : ";
cin >> sendBuf;
}
sendRet = send(serverfd, sendBuf, sizeof(sendBuf), 0);
if (sendRet == -1)
cout << "Error sending data\n";
if(strcmp(sendBuf, "y") == 0)
break;
}
// 关闭套接字
close(serverfd);
}
int main() {
// 创建套接字
int serverfd = socket(AF_INET, SOCK_STREAM, 0);
if (serverfd == -1) {
cerr << "Error creating socket\n";
return 1;
}
// 连接到服务器
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8080); // 与服务器相同的端口号
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr); // 这里使用本地回环地址,如果服务器在另一台机器上,请替换为服务器的IP地址
if (connect(serverfd, reinterpret_cast<struct sockaddr*>(&serverAddr), sizeof(serverAddr)) != -1) {
cout << serverfd << " : Connected successfully!" << endl;
connectToServer(serverfd);
} else {
cerr << "Error connecting to server\n";
close(serverfd);
return 1;
}
return 0;
}
服务端server.cpp:文章来源地址https://www.toymoban.com/news/detail-829363.html
- 一对一通信
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 1024
void connectToClient(int &clientfd) {
char recvBuf[SIZE]; // 接收缓冲区
char sendBuf[SIZE]; // 发送缓冲区
int recvRet, sendRet; // 接受反馈及发送反馈
while (clientfd != -1) {
// receive data
recvRet = recv(clientfd, recvBuf, sizeof(recvBuf), 0);
while(recvRet > 0) {
recvBuf[recvRet] = '\0';
if(strcmp(recvBuf, "$") == 0)
break;
cout << "recv data from client " << clientfd << ", data : " << recvBuf << endl;
recvRet = recv(clientfd, recvBuf, sizeof(recvBuf), 0);
}
// send data
cout << clientfd << ">> ";
cin >> sendBuf;
while(sendBuf) {
sendRet = send(clientfd, sendBuf, sizeof(sendBuf), 0);
if (strcmp(sendBuf, "$") == 0)
break;
if (sendRet == -1)
cout <<"send data error." << endl;
else
cout << "Sent data to client: " << sendBuf << endl;
cout << clientfd << ">> ";
cin >> sendBuf;
}
// 是否关闭连接
recvRet = recv(clientfd, recvBuf, sizeof(recvBuf), 0);
if (recvRet == -1)
cout << "Error receiving data\n";
else if(strcmp(recvBuf, "y") == 0) {
cout << "Have closed connection " << clientfd << '.' << endl;
close(clientfd);
break;
}
}
}
int main() {
// create socket
int serverfd = socket(AF_INET, SOCK_STREAM, 0); // IPV4 + TCP
if(-1 == serverfd) {
printf("create socket error");
return -1;
}
// bind port
struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(8080);
if(-1 == bind(serverfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) {
cout << "bind error" << endl;
return -1;
}
// start listen
if (listen(serverfd, 1024) == -1) {
printf("listem error");
return -1;
}
while (1) {
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
// accept connection
int clientfd = accept(serverfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
if(clientfd!= -1) {
// 获取客户端的信息
char cliIp[16];
inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));
unsigned short cliPort = ntohs(clientaddr.sin_port);
cout << "client ip is : " << cliIp << ", port is : " << cliPort << endl;
connectToClient(clientfd);
}
}
//close socket
close(serverfd);
return 0;
}
- 一对多通信(多进程)
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 1024
void connectToClient(int &clientfd) {
char recvBuf[SIZE]; // 接收缓冲区
char sendBuf[SIZE]; // 发送缓冲区
int recvRet, sendRet; // 接受反馈及发送反馈
while (clientfd != -1) {
// receive data
recvRet = recv(clientfd, recvBuf, sizeof(recvBuf), 0);
while(recvRet > 0) {
recvBuf[recvRet] = '\0';
if(strcmp(recvBuf, "$") == 0)
break;
cout << "recv data from client " << clientfd << ", data : " << recvBuf << endl;
recvRet = recv(clientfd, recvBuf, sizeof(recvBuf), 0);
}
// send data
cout << clientfd << ">> ";
cin >> sendBuf;
while(sendBuf) {
sendRet = send(clientfd, sendBuf, sizeof(sendBuf), 0);
if (strcmp(sendBuf, "$") == 0)
break;
if (sendRet == -1)
cout <<"send data error." << endl;
else
cout << "Sent data to client: " << sendBuf << endl;
cout << clientfd << ">> ";
cin >> sendBuf;
}
// 是否关闭连接
recvRet = recv(clientfd, recvBuf, sizeof(recvBuf), 0);
if (recvRet == -1)
cout << "Error receiving data\n";
else if(strcmp(recvBuf, "y") == 0) {
cout << "Have closed connection " << clientfd << '.' << endl;
close(clientfd);
break;
}
}
}
int main() {
// create socket
int serverfd = socket(AF_INET, SOCK_STREAM, 0); // IPV4 + TCP
if(-1 == serverfd) {
printf("create socket error");
return -1;
}
// bind port
struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(8080);
if(-1 == bind(serverfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) {
cout << "bind error" << endl;
return -1;
}
// start listen
if (listen(serverfd, 1024) == -1) {
printf("listem error");
return -1;
}
while (1) {
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
// accept connection
int clientfd = accept(serverfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
if(clientfd!= -1) {
// 获取客户端的信息
char cliIp[16];
inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));
unsigned short cliPort = ntohs(clientaddr.sin_port);
cout << "client ip is : " << cliIp << ", port is : " << cliPort << endl;
pid_t pid = fork();
if(pid == 0) // 子进程
connectToClient(clientfd);
}
}
//close socket
close(serverfd);
return 0;
}
到了这里,关于Linux网络编程——C++实现进程间TCP/IP通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!