Linux网络编程——C++实现进程间TCP/IP通信

这篇具有很好参考价值的文章主要介绍了Linux网络编程——C++实现进程间TCP/IP通信。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、函数说明

  • 地址接口

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:

#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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux【网络编程】TCP协议通信模拟实现、日志函数模拟、守护进程化、TCP协议通信流程、三次握手与四次挥手

    Tcp通信模拟实现与Udp通信模拟实现的区别不大,一个是面向字节流,一个是面向数据报;udp协议下拿到的数据可以直接发送,tcp协议下需要创建链接,用文件描述符完成数据的读写 1.1.1 接口认识 1.1.1.1 listen:监听socket 1.1.1.2 accept:获取连接 通信就用accept返回的文件描述符,

    2024年02月06日
    浏览(54)
  • Linux网络编程之TCP/IP实现高并发网络服务器设计指南

    目录 引言: 多进程服务器 例程分享: 多线程服务器  例程分享: I/O多路复用服务器 select 例程分享: poll 例程分享: epoll 例程分享: 总结建议         随着互联网的迅猛发展,服务器面临着越来越多的并发请求。如何设计一个能够高效处理大量并发请求的服务器成为

    2024年02月20日
    浏览(54)
  • Linux网络编程二(TCP图解三次握手及四次挥手、TCP滑动窗口、MSS、TCP状态转换、多进程/多线程服务器实现)

    1、TCP三次握手 TCP 三次握手 (TCP three-way handshake)是 TCP协议建立可靠连接 的过程,确保客户端和服务器之间可以进行可靠的通信。下面是TCP三次握手的 详细过程 : 假设客户端为A,服务器为B。 (1) 第一次握手 第一次握手(SYN=1,seq=500) A向B发送一个带有 SYN 标志位的数据包,

    2024年04月22日
    浏览(54)
  • 【Linux | 网络编程】TCP的服务端(守护进程) + 客户端

    上一节,我们用了udp写了一个服务端和客户端之间通信的代码,只要函数了解认识到位,上手编写是很容易的。 本章我们开始编写tcp的服务端和客户端之前通信的代码,要认识一批新的接口,并将我们之前学习的系统知识加进来,做到融会贯通… 代码详情:👉 Gitee 对于TC

    2024年01月16日
    浏览(46)
  • Linux 网络编程学习笔记——一、TCP/IP 协议族

    数据链路层实现了网卡接口的网络驱动程序,以处理数据在物理媒介(以太网、令牌环等)上的传输,不同的物理网络具有不同的电气特性,网络驱动程序隐藏了这些细节,为上层协议提供一个统一的接口。最常用的协议是 ARP(Address Resolve Protocol,地址解析协议)和 RARP(

    2024年02月02日
    浏览(74)
  • 多进程并发TCP服务器模型(含客户端)(网络编程 C语言实现)

    摘要 :大家都知道不同pc间的通信需要用到套接字sockte来实现,但是服务器一次只能收到一个客户端发来的消息,所以为了能让服务器可以接收多个客户端的连接与消息的传递,我们就引入了多进程并发这样一个概念。听名字就可以知道--需要用到进程,当然也有多线程并发

    2024年02月17日
    浏览(67)
  • 「网络编程」第二讲:socket套接字(四 - 完结)_ Linux任务管理与守护进程 | TCP协议通讯流程

    「前言」文章是关于网络编程的socket套接字方面的,上一篇是网络编程socket套接字(三),这篇续上篇文章的内容,下面开始讲解!  「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 「枫叶先生有点文青病」「句子分享」 Time goes on and on, never to an 

    2024年02月10日
    浏览(65)
  • 《TCP/IP网络编程》--基于TCP实现字符串对话和文件传输

    主要需求:         服务器端和客户端各传递 1 次字符串,基于 TCP 协议,传递字符串前先以 4 字节整数型方式传递字符串长度,剩余部分为字符串数据; 注:下面的代码基于 Windows 系统实现; 项目链接:Chapter5

    2024年02月09日
    浏览(55)
  • TCP/IP网络编程(1)——基于TCP的服务端和客户端的简单实现

    目录 前言 一、服务器端函数 1. 创建套接字函数 socket 2. 套接字绑定地址函数 bind 3. 等待连接请求函数 listen 4. 处理连接请求函数 accept 5. 关闭套接字函数 close 二、客户端函数 1. 请求连接函数 connect 三、完整代码 四、 基于TCP的半关闭 shutdown 五、 套接字可选项 getsockopt setsoc

    2024年02月07日
    浏览(57)
  • Socket网络编程(TCP/IP)实现服务器/客户端通信。

    一.前言 回顾之前进程间通信(无名管道,有名管道,消息队列,共享内存,信号,信号量),都是在同一主机由内核来完成的通信。 那不同主机间该怎么通信呢? 可以使用Socket编程来实现。 Socket编程可以通过网络来实现实现不同主机之间的通讯。 二.Socket编程的网络模型如

    2024年02月08日
    浏览(89)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包