Linux 网络编程 基础

一、套接字创建

1、套接字socket的创建

#include<sys/socket.h>

int socket(int domain, int type, int protocol);

→成功时返回文件描述符,失败时返回-1。
● domain套接字中使用的协议族(Protocol Family)信息。
● type套接字数据传输类型信息。
●protocol 计算机间通信中使用的协议信息。

参数一:domain

参数二:type

面向连接的套接字(SOCK_STREAM)---TCP

面向消息的套接字(SOCK DGRAM)---UDP

参数三:protocol  大部分情况下为0

除非遇到以下这种情况∶

"同一协议族中存在多个数据传输方式相同的协议"

数据传输方式相同,但协议不同。此时需要通过第三个参数具体指定协议信息

示例:

头文件
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>


int sock = socket(PF_INET,SOCK_STREAM,0);   //IPV4 TCP

2、bind函数

#include<sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
→成功时返回0,失败时返回-1。

参数一:套接字描述符  sockfd

要分配地址信息(IP地址和端口号)的套接字文件描述符。

参数二:存有地址信息的结构体变量地址值  myaddr

   网络地址

4字节地址族

IPv4(Internet Protocol version 4)

16字节地址族

IPv6(Internet Protocol version 6)

地址信息的表示
struct sockaddr_in 
{
sa_family//地址族(Address Family)
sa_family_t  sin_family;//  //地址族(Address Family)
uint16_t sin_port; // 16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用
}
该结构体中提到的另一个结构体in_addr定义如下,它用来存放32位IP地址。
struct in_addr
 {
In_addr_t s_addr; //32位IPv4地址
};

参数三:第二个结构体变量的长度

 一般直接用sizeof第二个参数就行

示例:

if(sock!=-1)
{
    stuct sockaddr_in addr;
    addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("192.168.0.1");  //IP
	addr.sin_port = htons(9527);  //端口

    bind(sock,(struct sockaddr*)&addr,sizeof(addr));
}

3、listen函数

#include <sys/socket.h>
int listen(int sock,int backlog);
→成功时返回0,失败时返回-1。
sock希望进入等待连接请求状态的套接字文件描述符
传递的描述符套接字参数成为服务器端 套接字(监听套接字)。
backlog 连接请求等待队列(Queue)的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列。

4、accept函数

#include<sys/socket.h>
int accept(int sock,struct sockaddr * addr, socklen_t*addrlen);
→成功时返回创建的套接字文件描述符,失败时返回-1。

5、connect函数

#include<sys/socket.h>
int connect(int sock,struct sockaddr*servaddr, socklen_t addrlen);
→成功时返回0,失败时返回-1。
● sock     客户端套接字文件描述符。
● servaddr 保存目标服务器端地址信息的变量地址值。
●addrlen  以字节为单位传递已传递给第二个结构体参数servaddr的地址变量长度。

二、TCP编程(基础,启动一次服务程序 只服务一个客户端)

基于TCP服务端/客户端的函数调用关系:

1、TCP服务端

最简单的流程 :

服务端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include <iostream>

void lession60()    //服务端
{
	int serv_sock, clnt_sock;  //创建两个套接字承载变量
	struct sockaddr_in serv_addr, clnt_addr;   //地址
	socklen_t clnt_addr_size;   //客户端地址长度

	const char* message = "Hello World!\n";

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);  //IPv4 TCP  创建套接字
	if (serv_sock < 0)   //发生错误
	{
		std::cout << "create socket failed!\n" << std::endl;
		return;
	}
	
	memset(&serv_addr, 0, sizeof(serv_addr));  //地址清0
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //本机地址
	serv_addr.sin_port = htons(9527);    //端口9527
	int ret = bind(serv_sock, (struct sockaddr*) & serv_addr, sizeof(serv_addr));  //绑定套接字
	if (ret == -1)
	{
		std::cout << "bind failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	ret = listen(serv_sock, 3);   //开始监听,队列3
	if(ret==-1)
	{
		std::cout << "listen failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	// accept 客户端连接    卡在此处!!!!!!
	clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
	if (clnt_sock == -1)
	{
		std::cout << "accept failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	//成功连接客户端

	//开始发信息给客户端
	ssize_t len = write(clnt_sock, message, strlen(message));
	     //注意上下两个ssize_t  如果用int会有warning!!
	if (len != (ssize_t)strlen(message))
	{
		std::cout << "write failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	close(clnt_sock);  //关闭客户端
	close(serv_sock);  //关闭服务端
}

2、TCP客户端

客户端代码:

#include <sys/wait.h>
void lession62()
{
	pid_t pid = fork();   //开一个子进程
	if (pid == 0)   //子进程
	{
		//开启客户端
		sleep(1);
		int client = socket(PF_INET, SOCK_STREAM, 0);  //创建套接字
		struct sockaddr_in servaddr;  //服务器的地址
		memset(&servaddr, 0, sizeof(servaddr));   //地址清0
		servaddr.sin_family = AF_INET;
		servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
		servaddr.sin_port = htons(9527);
		int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));  //连接服务器
		if (ret == 0)
		{
			char buffer[256] = "";
			read(client, buffer, sizeof(buffer));
			std::cout << buffer;
		}
		close(client);
		std::cout << "client done!" << std::endl;
	}
	else if (pid > 0)   //主进程
	{
		//开启服务端
		lession60();
		int status = 0;
		wait(&status);       //头文件 sys/wait.h
	}
	else
	{
		std::cout << "fork failed!" << pid << std::endl;
	}
}

int main(int argc, char* argv[])
{
	lession62();
	return 0;
}

三、迭代服务器

迭代服务器比较原始,原型为:

while(1)
{
new_fd = 服务器accept客户端的连接(new_fd = accept(listenfd, XX, XX))
逻辑处理
在这个new_fd上给客户端发送消息
关闭new_fd
}

代码:

//迭代服务器
#include <sys/wait.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include <iostream>

void lession63_()
{
	int serv_sock, clnt_sock;  //创建两个套接字承载变量
	struct sockaddr_in serv_addr, clnt_addr;   //地址
	socklen_t clnt_addr_size;   //客户端地址长度

	//const char* message = "Hello World!\n";

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);  //IPv4 TCP  创建套接字
	if (serv_sock < 0)   //发生错误
	{
		std::cout << "create socket failed!\n" << std::endl;
		return;
	}

	memset(&serv_addr, 0, sizeof(serv_addr));  //地址清0
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //本机地址
	serv_addr.sin_port = htons(9521);    //端口9523
	int ret = bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));  //绑定套接字
	if (ret == -1)
	{
		std::cout << "bind failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	ret = listen(serv_sock, 3);   //开始监听,队列3
	if (ret == -1)
	{
		std::cout << "listen failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	char buffer[1024]{};
	while (1)
	{
		memset(buffer, 0, sizeof(buffer));
		printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		// accept 客户端连接
		clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
		if (clnt_sock == -1)
		{
			std::cout << "accept failed!\n" << std::endl;
			close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
			return;
		}

		//成功连接客户端

		//开始发信息给客户端
		//ssize_t len = write(clnt_sock, message, strlen(message));
		ssize_t len = read(clnt_sock, buffer, sizeof(buffer));
		//注意上下两个ssize_t  如果用int会有warning!!
		if (len != (ssize_t)strlen(buffer))
		{
			std::cout << "read failed!\n" << std::endl;
			close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
			return;
		}
		std::cout << buffer;
		printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		len = write(clnt_sock, buffer, strlen(buffer));

		close(clnt_sock);  //关闭客户端
	}
	
	close(serv_sock);  //关闭服务端
}

void runClient()
{
	int client = socket(PF_INET, SOCK_STREAM, 0);  //创建套接字
	struct sockaddr_in servaddr;  //服务器的地址
	memset(&servaddr, 0, sizeof(servaddr));   //地址清0
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(9521);
	int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));  //连接服务器
	if (ret == 0)
	{
		char buffer[256] = "hello,here is client!\n";
		write(client, buffer, strlen(buffer));
		memset(buffer, 0, sizeof(buffer));
		read(client, buffer, sizeof(buffer));
		std::cout << buffer;
	}
	close(client);
	std::cout << "client done!" << std::endl;
}

void lession63()
{
	pid_t pid = fork();   //开一个子进程
	if (pid == 0)   //子进程
	{
		//开启客户端
		sleep(1);
		runClient();
		runClient();
	}
	else if (pid > 0)   //主进程
	{
		//开启服务端
		lession63_();
		int status = 0;
		wait(&status);       //头文件 sys/wait.h
	}
	else
	{
		std::cout << "fork failed!" << pid << std::endl;
	}
}

int main(int argc, char* argv[])
{
	/*lession62();*/
	lession63();
	return 0;
}

四、回声服务器

1、基础部分

服务器接收到客户端的数据,直接把客户端的数据发回客户端,代码:

//头文件
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include <iostream>

客户端:

void run_client64()  //客户端
{
	int client = socket(PF_INET, SOCK_STREAM, 0);  //创建套接字
	struct sockaddr_in servaddr;  //服务器的地址
	memset(&servaddr, 0, sizeof(servaddr));   //地址清0
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(9527);
	int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));  //连接服务器
	while (ret == 0)
	{
		//char buffer[256]{ "hello,here is client!\n" };
		//write(client, buffer, strlen(buffer));   //常量 写死 
		printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		/*不用常量,可以输入的写法*/
		char buffer[256] = "";
		fputs("Input message(Q/q to quit):", stdout);  //提示输出,按q退出
		fgets(buffer, sizeof(buffer), stdin);  //输入
		if ( (strcmp(buffer, "q\n") == 0) || (strcmp(buffer, "Q\n") == 0) )
		{
			break;  //退出循环,准备关闭客户端
		}
		write(client, buffer, strlen(buffer));
		/*                       */
		memset(buffer, 0, sizeof(buffer));
		read(client, buffer, sizeof(buffer));
		std::cout << "from server:" << buffer;
	}
	close(client);
	std::cout << "client done!" << std::endl;
}

服务端:

void server64()  //服务端
{
	int serv_sock, client;  //创建两个套接字承载变量
	struct sockaddr_in serv_addr, clnt_addr;   //地址
	socklen_t clnt_addr_size;   //客户端地址长度

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);  //IPv4 TCP  创建套接字
	if (serv_sock < 0)   //发生错误
	{
		std::cout << "create socket failed!\n" << std::endl;
		return;
	}

	memset(&serv_addr, 0, sizeof(serv_addr));  //地址清0
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //本机地址
	serv_addr.sin_port = htons(9527);    //端口9523
	int ret = bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));  //绑定套接字
	if (ret == -1)
	{
		std::cout << "bind failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	ret = listen(serv_sock, 3);   //开始监听,队列3
	if (ret == -1)
	{
		std::cout << "listen failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	char buffer[1024]{};
	for (int i = 0; i < 2; i++) //服务器不是无限制的 只能服务两个客户端后结束
	{
		printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		// accept 客户端连接
		client = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
		if (client == -1)
		{
			std::cout << "accept failed!\n" << std::endl;
			close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
			return;
		}

		//成功连接客户端

		/*    printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		//开始从客户端读信息
		memset(buffer, 0, sizeof(buffer));
		ssize_t len = read(client, buffer, sizeof(buffer));
		if (len <= 0)
		{
			std::cout << "read failed!\n" << std::endl;
			close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
			return;
		}
		else
		{
			std::cout <<"from client:"<< buffer << std::endl;
		}

		//开始发信息给客户端
		len = write(client, buffer, len);  // 第二个len就是strlen(buffer)
		//注意上下两个ssize_t  如果用int会有warning!!
		if (len != (ssize_t)strlen(buffer))
		{
			std::cout << "write failed!\n" << std::endl;
			close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
			return;
		}
		else
		{
			std::cout << "write to client:" << buffer << std::endl;
		}
		*/
		/*一种精简的写法*/
		memset(buffer, 0, sizeof(buffer));
		ssize_t len{}; 
		while ( ( len = read(client, buffer, sizeof(buffer)) )>0 ) //读到了
		{
			//std::cout << "from client:" << buffer << std::endl;  //输出内容

			//开始发信息给客户端
			len = write(client, buffer, len);  // 第二个len就是strlen(buffer)
			//注意上下两个ssize_t  如果用int会有warning!!
			if (len != (ssize_t)strlen(buffer))
			{
				std::cout << "write failed!\n" << std::endl;
				close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
				return;
			}
			memset(buffer, 0, sizeof(buffer));
		}

		/*if (len <= 0)   //没读到
		{
			std::cout << "read failed!\n" << std::endl;
			close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
			return;
		}*/     //只是一个客户端出问题,没必要关闭服务器,也没必要return
		close(client);  //关闭客户端
	}

	close(serv_sock);  //关闭服务端
	printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
}

服务端客户端联调:

void lession64()
{
	pid_t pid = fork();   //开一个子进程
	if (pid == 0)   //子进程
	{
		//开启服务器
		server64();
	}
	else if (pid > 0)   //主进程
	{
		//开启客户端
		sleep(1);
		for (int i = 0; i < 2; ++i)
		{
			run_client64();
		}
		int status = 0;
		wait(&status);       //头文件 sys/wait.h
	}
	else
	{
		std::cout << "fork failed!" << pid << std::endl;
	}
}


int main(int argc, char* argv[])
{
	lession64();
	return 0;
}

2、存在的问题及解决方案

回收服务器存在的问题:

        定义的buffer为1024, 字符串太长,需要分2个或多个数据包发送时,返回的len会不对。

完美解决以上问题:

修改后:(多次write和read,方便打包发送)

void run_client64()  //客户端
{
	int client = socket(PF_INET, SOCK_STREAM, 0);  //创建套接字
	struct sockaddr_in servaddr;  //服务器的地址
	memset(&servaddr, 0, sizeof(servaddr));   //地址清0
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(9527);
	int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));  //连接服务器
	while (ret == 0)
	{
		//char buffer[256]{ "hello,here is client!\n" };
		//write(client, buffer, strlen(buffer));   //常量 写死 
		printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		/*不用常量,可以输入的写法*/
		char buffer[256] = "";
		fputs("Input message(Q/q to quit):", stdout);  //提示输出,按q退出
		fgets(buffer, sizeof(buffer), stdin);  //输入
		if ((strcmp(buffer, "q\n") == 0) || (strcmp(buffer, "Q\n") == 0))
		{
			break;  //退出循环,准备关闭客户端
		}

		size_t len = strlen(buffer);  //!!!!!!!!!!!修改的
		size_t send_len{ 0 };   //!!!!!!!!!!!
		while (send_len < len)    //!!!!!!!!!!!以下!
		{
			ssize_t ret = write(client, buffer + send_len, len - send_len);
			if (ret <= 0)
			{
				fputs("write failed!\n", stdout);
			
				close(client);
				std::cout << "client done!" << std::endl;
				return;
			}
			send_len += (size_t)ret;       
		}
		/*                       */
		memset(buffer, 0, sizeof(buffer));
		size_t read_len = 0;
		while (read_len < len)
		{
			ssize_t ret = read(client, buffer + read_len, len-read_len);
			if (ret <= 0)
			{
				fputs("read failed!\n", stdout);
				ret = -1;
				close(client);
				std::cout << "client done!" << std::endl;
				return;
			} 
			read_len += (size_t)ret;
		}
		std::cout << "from server:" << buffer;
	}
	close(client);
	std::cout << "client done!" << std::endl;
}

五、回声服务器实战:计算器的网络实现

要求:

1、客户端连接到服务器端后以1字节整数形式传递待算数字个数。

2、客户端向服务器端传递的每个整数型数据占用4字节。

3、传递整数型数据后接着传递运算符。运算符信息占用1字节。

      选择字符+、-、*之一传递。

4、服务器端以4字节整数型向客户端传回运算结果。

5、客户端得到运算结果后终止与服务器端的连接。

代码:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include <iostream>

int calculate(int count, int oprand[], char op)
{
	int result{0};
	switch (op)
	{
	case'+':
		for (int i=0;i<count;i++)
		{
			result += oprand[i];
		}
		break;
	case'-':
		for (int i = 0; i < count; i++)
		{
			result -= oprand[i];
		}
		break;
	case'*':
		result = 1;
		for (int i = 0; i < count; i++)
		{
			result *= oprand[i];
		}
		break;
	default:
		break;
	}
	return result;
}

void server66()
{
	int serv_sock, client;  //创建两个套接字承载变量
	struct sockaddr_in serv_addr, clnt_addr;   //地址
	socklen_t clnt_addr_size;   //客户端地址长度

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);  //IPv4 TCP  创建套接字
	if (serv_sock < 0)   //发生错误
	{
		std::cout << "create socket failed!\n" << std::endl;
		return;
	}

	memset(&serv_addr, 0, sizeof(serv_addr));  //地址清0
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //本机地址
	serv_addr.sin_port = htons(9523);    //端口9523
	int ret = bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));  //绑定套接字
	if (ret == -1)
	{
		std::cout << "bind failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	ret = listen(serv_sock, 3);   //开始监听,队列3
	if (ret == -1)
	{
		std::cout << "listen failed!\n" << std::endl;
		close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
		return;
	}

	char buffer[1024]{};
	for (int i = 0; i < 2; i++) //服务器不是无限制的 只能服务两个客户端后结束
	{
		//printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		// accept 客户端连接
		client = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
		if (client == -1)
		{
			std::cout << "accept failed!\n" << std::endl;
			close(serv_sock);  //!!!!!  套接字已经启用 需要先关闭才能return
			return;
		}

		//成功连接客户端


		/*一种精简的写法*/
		memset(buffer, 0, sizeof(buffer));
		ssize_t len{};
		len = read(client, buffer, 1);
		int result{};
		if (len > 0)
		{                  //必须&因为16进制转无符号前面会填满1
			for (unsigned i = 0; i < ((unsigned)buffer[0] & 0xFF); i++)
			{
				read(client, buffer + 1 + i * 4, 4);
			}
			read(client, buffer + 1 + ((unsigned)buffer[0] & 0xFF)*4, 1);
			result = calculate(((int)buffer[0] & 0xFF), (int*)(buffer + 1), 
				buffer[1 + ((unsigned)buffer[0] & 0xFF) * 4]);
			write(client, &result, 4);  //发回客户端
		}
		close(client);  //关闭客户端
	}

	close(serv_sock);  //关闭服务端
	printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
}

void run_client66()
{
	int client = socket(PF_INET, SOCK_STREAM, 0);  //创建套接字
	struct sockaddr_in servaddr;  //服务器的地址
	memset(&servaddr, 0, sizeof(servaddr));   //地址清0
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(9523);
	int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));  //连接服务器

	char buffer[1024]{};
	while (ret == 0)
	{
		//printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
		/*不用常量,可以输入的写法*/
		fputs("operand count:", stdout);  //提示要输入的操作数的数量
		int opnd_cnt{ 0 };   //变量保存操作数的数量
		scanf("%d", &opnd_cnt);

		if (opnd_cnt <= 1 && opnd_cnt >= 256)
		{
			fputs("opnd_cnt error,too small or too big!\n", stdout);
			close(client);
			std::cout << "client done!" << std::endl;
			return;
		}

		buffer[0] = (char)opnd_cnt;   //服务器此处要解释为无符号
		for (int i = 0; i < opnd_cnt; ++i)
		{                                     //!!!!!!!!!!!!!!!!!!!!!
			scanf("%d", buffer + 1 + i * 4);  //*  **** **** 一个是符号1字节 后面是数字4字节
		}                                     //!!!32位环境int是4字节!!64位为8字节!!!
		fgetc(stdin);   //上面输入完需要加一个换行符 但不取!! 很细节
		fputs("Operator:", stdout);  //提示输入操作符
		buffer[1 + opnd_cnt * 4] = (char)fgetc(stdin);

		size_t len = opnd_cnt * 4 + 2;  //2  是 前面一个字符 后面一个字符
		size_t send_len{ 0 };
		while (send_len < len)
		{
			ssize_t ret = write(client, buffer + send_len, len - send_len);
			if (ret <= 0)
			{
				fputs("write failed!\n", stdout);

				close(client);
				std::cout << "client done!" << std::endl;
				return;
			}
			send_len += (size_t)ret;
		}
		/*                       */
		memset(buffer, 0, sizeof(buffer));
		size_t read_len = 0;
		while (read_len < 4)  //最后写的是4个字节,因此读4个字节即可
		{
			ssize_t ret = read(client, buffer + read_len, len - read_len);
			if (ret <= 0)
			{
				fputs("read failed!\n", stdout);
				ret = -1;
				close(client);
				std::cout << "client done!" << std::endl;
				return;
			}
			read_len += (size_t)ret;
		}
		printf("from server:%d\n", * (int*)buffer);
	}
	close(client);
	std::cout << "client done!" << std::endl;
}

void lession66()
{
	pid_t pid = fork();   //开一个子进程
	if (pid == 0)   //子进程
	{
		//开启服务器
		server66();
	}
	else if (pid > 0)   //主进程
	{
		//开启客户端
		sleep(1);
		for (int i = 0; i < 2; ++i)
		{
			run_client66();
		}
		int status = 0;
		wait(&status);       //头文件 sys/wait.h
	}
	else
	{
		std::cout << "fork failed!" << pid << std::endl;
	}
}



int main(int argc, char* argv[])
{
	lession66();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yy_xzz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值