Linux网络编程(5)——多进程、多线程实现TCP简易中英文翻译

SOCKET:

//封装的形式实现

#include <stdio.h>
#include <cstdio>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <cstringt>
#include <sys/socket.h> //socket相关
#include <netinet/in.h>
#include <arpa/inet.h>//htons()
#include <cstringt> //sendto()  


class TcpSocket
{
public:
	TcpSocket() :fd_(-1){


	}


	bool Socket() {
		//和 UDP 不一样是第二个函数 SOCK_STREAM 意思面向字节流
		fd_ =socket(AF_INET,SOCK_STREAM,0);
			if (fd_ < 0) {
				perror("socket");
				return false;
			}
		return true;
	}

	//给服务器
	bool Bind(const std::string& ip,uint16_t port) {
		sockaddr_in addr;
		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = inet_addr(ip.c_str());
		addr.sin_port = htons(port);
		int ret = bind(fd_, (sockaddr*)&addr, sizeof(addr));
		if (ret<0) {
			perror("bind");
			return false;
		}
		return true;
	}

	//给服务器
	//进入监听状态
	bool Listen() {
		//int listen(int socket, int backlog);
	    //int socket:文件描述符
		//int backlog:队列的长度
		int ret=int listen(fd_, 10);
		if (ret<0) {
			perror("listen");
			return false;
		}
		return true;
	}

	//给服务器
	bool Accept(TcpSocket* peer, std::string& ip = NULL, uint16_t port=NULL) {
		//int accept(int socket, struct sockaddr* address, socklen_t* address_len);
		//struct sockaddr* address:对端的 ip 地址,端口号。
		//从连接队列中去一个连接到用户代码中,如果队列中没有连接,阻塞(默认行为)

		sockaddr_in peer_addr;
		socklen_t len = sizeof(peer_addr); //设置初始值
		//返回值也是一个socket,类似内场销售
		int client_socket = accept(fd_, (sockaddr*)&peer_addr, &len);
		if (client_socket < 0) {
			perror("accept");
			return false;
		}
		peer->fd_ = client_socket;
		if (ip !=NULL) {
			*ip = inet_ntoa(peer_addr.sin_addr);
		}
		if (port != NULL) {
			*port = ntohs(peer_addr.sin_port);
		}
		return true;
	}

	//给客户端+服务器
	int Recv(std::string& msg) {
		msg->clear();
		char buf[1024 * 10] = { 0 };
		ssize_t n = recv(fd_, buf, sizeof(buf)-1, 0);
		//返回值成功:返回读到的字节数;失败,返回-1,如果是对端关闭了socket,但会结果是 0 。
		if (n<0) {
			perror("recv");
			return -1;
		}
		else if (n==0)
		{
			return 0;

		}
		msg->assign(buf);  //读到的数据赋值到msg中
		return 1;
		
	}

	//给客户端+服务器
	bool Send(const std::string& msg) {
		ssize_t n = send(fd_, msg.c_str(), msg.size(), 0);
		if (n < 0) {
			perror("send");
			return false;
		}
		return true;
	}

	//给客户端
	bool Connect(const std::string& ip, uint16_t port) {
		sockaddr_in addr;
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr(ip.c_str());
		int ret = connect(fd_, (sockaddr*)&addr, sizeof(addr));
		if (ret < 0) {
			perror("connect");
			return false;
		}
		return true;
	}

	bool Close() {
		if (fd_ != 1) {
			close(fd_);
		}
		return true;
	}

private:
	int fd_;
};

TCP服务端封装:(多进程)

#include "TCPSOCKET.hpp"

#include <functional>
#include <cassert>
typedef std::function<void(const std::string&, std::string*)> Handler;
#define CHECK_RET(exp)if(!(exp)) {\
	return false; \
}


class TcpProcessServer {
public:
	TcpProcessServer() {

	}
	~TcpProcessServer() {
		listen_sock_.Close();
	}

	bool Start(const std::string& ip, uint16_t port,Handler handler) {
		//1.创建socket
		CHECK_RET(listen_sock_.Socket());

		//2.绑定端口号
		CHECK_RET(listen_sock_.Bind(ip, port));

		//3.监听
		CHECK_RET(listen_sock_.Listen());

		//4.进入主循环
		while (true) {
			//5.调用Accept
			TcpSocket client_sock;
			std::string peer_ip;
			std::string peer_port;
			bool ret = listen_sock_.Accept(&listen_sock_,&peer_ip, &peer_port);
			if (!ret) {
				continue;
			}
			printf("[%s:%d] 客户端建立连接!\n", peer_ip, peer_port);

			//6.创建子进程,让子进程回应客户端请求,父进程继续调用Accept
			ProcessConnect(client_sock,peer_ip,peer_port,handler);
		}
	}

private:
	TcpSocket listen_sock_;

	void ProcessConnect(TcpSocket& clicent_sock, const std::string& ip, uint16_t port, Handler handler) {
		//1.创建子进程
		pid_t ret = fork();
		//2.父进程结束函数
		if (ret > 0) {
			//父进程
			//父进程也需要关闭这个slcket
			//否则文件描述符泄露
			clicent_sock.Close();
			return;
		}
		//3.子进程循环的和客户端交互:三件事
		//	a.读取客户端请求
		while (true) {
			std::string req;
			int r = clicent_sock.Recv(&req);  
			if (r < 0) {
				continue;
			}
			if (r==0) {
				printf("[%s:%d] 客户端断开连接\n", ip.c_str(), port);
				break;
			}
		}
		
		//	b.计算响应
		std::string resp;
		handler(req, resp);
		//	c.响应反馈给客户端
		clicent_sock.Send(resp);
	}
	//子进程的收尾工作
	//1.关闭socket
	clicent_sock.Close();
	//2.结束进程
	exit(0);
};

 TCP服务端封装:(多线程)

#include "TCPSOCKET.hpp"

#include <functional>
#include <cassert>
#include<singal.h>
#include <pthread.h>
typedef std::function<void(const std::string&, std::string*)> Handler;

#define CHECK_RET(exp)if(!(exp)) {\
	return false; \
}


class TcpProcessServer {
public:
	TcpProcessServer() {

	}
	~TcpProcessServer() {
		listen_sock_.Close();
	}

	bool Start(const std::string& ip, uint16_t port, Handler handler) {


		//1.创建socket
		CHECK_RET(listen_sock_.Socket());

		//2.绑定端口号
		CHECK_RET(listen_sock_.Bind(ip, port));

		//3.监听
		CHECK_RET(listen_sock_.Listen());

		//4.进入主循环
		while (true) {
			//5.调用Accept
			TcpSocket client_sock;
			std::string peer_ip;
			std::string peer_port;
			bool ret = listen_sock_.Accept(&listen_sock_, &peer_ip, &peer_port);
			if (!ret) {
				continue;
			}
			printf("[%s:%d] 客户端建立连接!\n", peer_ip, peer_port);

			//6.创建线程,让新线程回应客户端请求,主线程继续调用Accept
			ProcessConnect(client_sock, peer_ip, peer_port, handler);
		}
	}

private:
	TcpSocket listen_sock_;

	struct ThreadEntryArg 
	{
		TcpSocket& clicent_sock, 
		std::string ip, 
		uint16_t port, 
		Handler handler
	};

	void ProcessConnect(TcpSocket& clicent_sock, const std::string& ip, uint16_t port, Handler handler) {
		//1.创建线程
		pthread_t tid;
		ThreadEntryArg* arg = new ThreadEntryArg;
		arg.clicent_sock = clicent_sock;
		ard.ip = ip;
		arg.port = port;
		arg.handler = handler;
		pthread_creat(tid, NULL, ThreadEntry, arg);

		//2.主线程返回
		pthread_detach(tid);

		//3.新线程循环的和客户端交互:三件事

	}
	TcpSocket& clicent_sock, const std::string& ip, uint16_t port, Handler handler
	static void* ThreadEntry(void* arg) {
		ThreadEntryArg* argument = (ThreadEntryArg*)arg;
		TcpSocket clicent_sock = argument->clicent_sock;
		std::string& ip = argument->ip;
		uint16_t port = argument->port;
		Handler handler = argument->handler
		while (true) {
			//	a.读取客户端请求
			
				std::string req;
				int ret = clicent_sock.Recv(&req);
				if (ret < 0) {
					continue;
				}
				if (ret == 0) {
					printf("[%s:%d] 客户端断开连接\n", ip.c_str(), port);
					clicent_sock.Close();
					break;
				}
		
			//	b.计算响应
			printf("[%s:%d] 客户端请求响应\n", ip.c_str(), req.c_str());
			std::string resp;
			handler(req, resp);
			//	c.响应反馈给客户端
			clicent_sock.Send(resp);
		}
		delete argument;
	}
};

中英文翻译服务端:

//#include "封装TCP服务端.hpp"
#include <unordered_map>
//#include "封装多进程TCP服务端.hpp"
#include "封装多线程TCP服务端.hpp"
int main() {
	TcpProcessServer server;
	std::unordered_map<std::string, std::string> dict;
	dict.insert(std::make_pair("heallo", "你好"));
	dict.insert(std::make_pair("world", "世界"));
	dict.insert(std::make_pair("bit", "比特"));
	server.Start("0.0.0.0", 9090, [](const std::string& req, std::string* resp) {
		auto it = dict.find(req);
		if (it == dict.end()) {
			*resp = "未找到";
		}
		else {
			*resp = it->second;
		}
	});
	system("pause");
	return 0;
}

TCP封装客户端:

#include "TCPSOCKET.hpp"

class TcpClient {
public:
	TcpClient() {
		sock_.Socket();
	}
	~TcpClient() {
		sock_.Close();
	}

	bool Connect(const std::string& ip, uint16_t port) {
		return sock_.Connect(ip,port);
	}

	int Recv(std::string* msg) {
		return sock_.Recv(msg);
	}

	bool Send(const std::string& msg) {
		return sock_.Send(msg);
	}
	
private:
	TcpSocket sock_;
};

中英文翻译客户端:

#include "封装的TCP客户端.hpp"
 


int main(int argc,char* argv[]) {
	if (argc !=2) {
		printf("Usage ./dict_client [ip]\n");
		return 1;
	}
	
	TcpClient clicent;
	bool ret = clicent.Connect(argv[1], 9090);
	if (!ret) {
		return 1;
	}
 	while (true) {
 		//从标准输入数据
		printf("请输入要查询的单词:\n");
		fflush(stdout);
		char req[1024] = { 0 };
		scanf("%s", req);

		//把读到的数据给服务器 
		clicent.Send(req);
		std::string resp;

		//读取服务器响应的结果
		clicent.Recv(&resp);

		//把响应的结果打印到标准输出上
		printf("resp: %s\n", resp.c_str());
 	}
 	system("pause");
 	return 0;
 }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目简介: 采用I/O复用技术select实现socket通信,采用多线程负责每个客户操作处理,完成Linux下的多客户聊天室! OS:Ubuntu 15.04 IDE:vim gcc make DB:Sqlite 3 Time:2015-12-09 ~ 2012-12-21 项目功能架构: 1. 采用client/server结构; 2. 给出客户操作主界面(注册、登录、帮助和退出)、登录后主界面(查看在线列表、私聊、群聊、查看聊天记录、退出); 3. 多客户可同时连接服务器进行自己操作; ##服务器端## 1. server.c:服务器端主程序代码文件; 2. config.h:服务器端配置文件(包含需要的头文件、常量、数据结构及函数声明); 3. config.c:服务器端公共函数的实现文件; 4. list.c:链表实现文件,用于维护在线用户链表的添加、更新、删除操作; 5. register.c:服务器端实现用户注册; 6. login.c:服务器端实现用户登录; 7. chat.c:服务器端实现用户的聊天互动操作; 8. Makefile:服务器端make文件,控制台执行make命令可直接生成可执行文件server ##客户端## 1. client.c:客户端主程序代码文件; 2. config.h:客户端配置文件(包含需要的头文件、常量、数据结构及函数声明); 3. config.c:客户端公共函数的实现文件; 4. register.c:客户端实现用户注册; 5. login.c:客户端实现用户登录; 6. chat.c:客户端实现用户的聊天互动操作; 7. Makefile:客户端make文件,控制台执行make命令可直接生成可执行文件client;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值