网络编程套接字——TCP

一、单执行流(服务完了一个才能回应另一个客户端)

tcp_client.hpp

#pragma once

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

class TcpClient
{
private:
	std::string svr_ip;
	int svr_port;
	int sock;
public:
	TcpClient(std::string _ip,int _port):svr_ip(_ip),svr_port(_port),sozk(-1)
	{}
	bool InitTcpClient()
	[
		sock = socket(AF_INET,SOCK_STREAM,0);
		if(sock < 0)  //创建失败
		{
			std::cerr << "socket error" << std::endl;
			return false;
		}
		//不需要绑定,不需要监听
		return true;
	}

	//需要连接服务器
	void Start()
	{
		struct sockaddr_in peer;
		memset(&peer,0,sizeof(peer));
		peer.sin_family = AF_INET;
		peer.sin_port = htons(svr_port);
		peer.sin_addr.s_addr = inet_addr(svr_ip.c_str());
		
		if(connect(sock,(struct sockaddr*)&peer,sizeof(peer)) == 0)
		{
			//success
			std::cout << "connect success..." << std::endl;
			Request(sock);
		}
		else
		{
			//fail
			std::cout << "connect fail..." << std::endl;
		}
	}
	void Request(int sock)
	{
		std::string message;
		char buffer[1024];
		while(true)
		{
			std::cout << "Please Enter#";
			std::cin >> message;  //输入消息

			write(sock,message.c_str(),message.size());  //发过去
			ssize_t s = read(sock,buffer,sizeof(buffer)-1); //读取发送过来的数据
			if(s > 0)
			{
				buffer[s] = 0;
				std::cout << "server echo#" << buffer << std::endl;
			}
		}
	}
	~TcpClient()
	{
		if(sock >= 0)
			close(sock);
	}
};

tcp_client.cc

#include "tcp_client.hpp"

void Usage(std::string proc)
{
	std::cout << "Usage: " << proc << "server_ip server_port" << std:;endl;
}
//  3个参数 :./client  server_ip   server_port
int main(int argc, char *argv[])
{
	if(argc != 3)
	{
		Usage(argv[0]);
		return 1;
	}
	TcpClient tcli(argv[1],atoi(argv[2]));
	tcli.InitTcpClient();
	tcli.Start();
	return 0;
}

tcp_server.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define BACKLOG 5  //全连接队列长度
class TcpServer
{
private:
	int port;
	int listen_sock;  //tcp是面向连接的,需要socket,监听套接字
public:
	TcpServer(int _port):port(_port),listen_sock(-1)
	{}
	bool InitTcpServer()
	{
		listen_sock = socket(AF_INET,SOCK_STREAM,0);
		if(listen_sock < 0)  //创建套接字失败 
		{
			std::cerr << "socket error" << std::endl;
			return false;
		}
		
		//绑定
		struct sockaddr_in local;
		memset(&local,0,sizeof(local));  //初始化
		local.sin_family = AE_INET;
		local.sin_port = hons(port);
		local.sin_addr.s_addr = INADDR_ANY;
		
		if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)  //绑定失败
		{
			std::cerr << "bind error" << std::endl;
			return false;
		}
		
		//有连接到来,需要监听
		if(listen(listen_sock,BACKLOG) < 0)  //监听失败
		{
			std::cerr << "listen error" << std::endl;
			return false;
		}
		return true;
	}
	void Loop()
	{ 
		for(;;)
		{
			//获取连接,知道连接的scoket与内容
			struct sockaddr_in peer;
			socklen_t len = sizeof(peer);
			int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);  //返回值是一个文件描述符  
			if(sock < 0)
			{
				std::cerr << "accept error" << std:;endl;
				continue;  //没有获取新连接成功,则继续获取
			}
			//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
			//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
			std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;  

			//处理请求
			std::string ip = inet_ntoa(peer.sin_addr);
			int port = ntohs(peer.sin_port);
			std::cout << "get a new link [" << ip << "]" << std::endl;
			Service(sock,ip,port);
		}
	}
	void Service(int sock,std::string ip,int port)
	{
		char buffer[1024];
		while(true)
		{
			ssize_t size = read(sock,buffer,sizeof(buffer)-1);
			if(size > 0)
			{
				buffer[size] = 0;
				std::cout << ip <<":" << port <<"#" << buffer << std::endl;
				write(sock,buffer,size);
			}
			else if(size == 0)
			{
				std::cout << ip <<":" << port <<"close" << std::endl;
				break;
			}
			else
			{
				std::cerr << sock << "read error" << std::endl;
				break;
			}
		}
		close(sock);
		std:;cout << "serice done" << std::endl;
	}
	~TcpServer()
	{
		if(listen_sock >= 0)
			close(listen_sock);
	}
};

tcp_server.cc

#include "tcp_server.hpp"
void Usage(std::string proc)
{
	std::cout << "Usage: " << proc << " port" << std::endl;
}
int main(int argc,char *argv[])
{
	if(argc != 2)
	{
		Usage(argv[0]);
		return 1;
	}
	//TcpServer tsvr(8081);
	TcpServer tsvr(atoi(argv[1]));   //argv[1]为程序名称
	tsvr.InitTcpServer();

	tsvr.Loop();
}

注意:
(1)accept函数 返回的是一个文件描述符,sock与listen_sock的联系,listen_sock相当于拉客的店小二,sock相当于进入饭店后为客人服务的服务员
listen_sock:获取新连接
sock:服务新连接(读取数据、分析数据、写入数据)
(2)ssize_t read(int fd,coid *buf,size_t count)
ssize > 0 :实际读取的字节
ssize_t == 0: 说明对端把链接关闭
ssize_t < 0:说明读取时遇到了错误
(3)

二、多执行流

创建fork(),但是进程是阻塞的,解决方法如下:
法一:利用signal,

tcp_server.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>

#define BACKLOG 5  //全连接队列长度
class TcpServer
{
private:
	int port;
	int listen_sock;  //tcp是面向连接的,需要socket,监听套接字
public:
	TcpServer(int _port):port(_port),listen_sock(-1)
	{}
	bool InitTcpServer()
	{
		listen_sock = socket(AF_INET,SOCK_STREAM,0);
		if(listen_sock < 0)  //创建套接字失败 
		{
			std::cerr << "socket error" << std::endl;
			return false;
		}
		
		//绑定
		struct sockaddr_in local;
		memset(&local,0,sizeof(local));  //初始化
		local.sin_family = AE_INET;
		local.sin_port = hons(port);
		local.sin_addr.s_addr = INADDR_ANY;
		
		if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)  //绑定失败
		{
			std::cerr << "bind error" << std::endl;
			return false;
		}
		
		//有连接到来,需要监听
		if(listen(listen_sock,BACKLOG) < 0)  //监听失败
		{
			std::cerr << "listen error" << std::endl;
			return false;
		}
		return true;
	}
	void Loop()
	{ 
		signal(SIGCHLD,SIG_IGN);  //会自动释放退出进程
		//获取连接,知道连接的scoket与内容
		struct sockaddr_in peer;
		for( ; ; )
		{
			socklen_t len = sizeof(peer);
			int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);  //返回值是一个文件描述符  
			if(sock < 0)
			{
				std::cerr << "accept error" << std:;endl;
				continue;  //没有获取新连接成功,则继续获取
			}
			//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
			//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
			std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;  

			//处理请求
			std::string ip = inet_ntoa(peer.sin_addr);
			int port = ntohs(peer.sin_port);
			std::cout << "get a new link [" << ip << "]" << std::endl;
			pid_t id = fork();   //让子进程提供服务,子进程会继承父进程文件描述符
			if(id == 0)
			{
				//child 管道
				Service(sock,ip,port);
			}
			//waitpid();  //阻塞的,非阻塞的话需要保存所有进程信息
		}
	}
	void Service(int sock,std::string ip,int port)
	{
		char buffer[1024];
		while(true)
		{
			ssize_t size = read(sock,buffer,sizeof(buffer)-1);
			if(size > 0)
			{
				buffer[size] = 0;
				std::cout << ip <<":" << port <<"#" << buffer << std::endl;
				write(sock,buffer,size);
			}
			else if(size == 0)
			{
				std::cout << ip <<":" << port <<"close" << std::endl;
				break;
			}
			else
			{
				std::cerr << sock << "read error" << std::endl;
				break;
			}
		}
		close(sock);
		std:;cout << "serice done" << std::endl;
	}
	~TcpServer()
	{
		if(listen_sock >= 0)
			close(listen_sock);
	}
};

法二:利用孤儿进程由OS回收
tcp_server.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<sys/wait.h>

#define BACKLOG 5  //全连接队列长度
class TcpServer
{
private:
	int port;
	int listen_sock;  //tcp是面向连接的,需要socket,监听套接字
public:
	TcpServer(int _port):port(_port),listen_sock(-1)
	{}
	bool InitTcpServer()
	{
		listen_sock = socket(AF_INET,SOCK_STREAM,0);
		if(listen_sock < 0)  //创建套接字失败 
		{
			std::cerr << "socket error" << std::endl;
			return false;
		}
		
		//绑定
		struct sockaddr_in local;
		memset(&local,0,sizeof(local));  //初始化
		local.sin_family = AE_INET;
		local.sin_port = hons(port);
		local.sin_addr.s_addr = INADDR_ANY;
		
		if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)  //绑定失败
		{
			std::cerr << "bind error" << std::endl;
			return false;
		}
		
		//有连接到来,需要监听
		if(listen(listen_sock,BACKLOG) < 0)  //监听失败
		{
			std::cerr << "listen error" << std::endl;
			return false;
		}
		return true;
	}
	void Loop()
	{ 
		//第一种方法:signal(SIGCHLD,SIG_IGN);  //会自动释放退出进程
		//获取连接,知道连接的scoket与内容
		struct sockaddr_in peer;
		for( ; ; )
		{
			socklen_t len = sizeof(peer);
			int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);  //返回值是一个文件描述符  
			if(sock < 0)
			{
				std::cerr << "accept error" << std:;endl;
				continue;  //没有获取新连接成功,则继续获取
			}
			//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
			//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
			std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;  

			//处理请求
			pid_t id = fork();  //爷爷  爸爸   孙子
			if(id == 0)
			{
				//child 管道
				if(fork()> 0) 
				{
					exit(0);  //父进程直接终止,提供服务的是孙子进程,成为孤儿进程,直接由OS回收
				}
				std::string ip = inet_ntoa(peer.sin_addr);
				int port = ntohs(peer.sin_port);
				std::cout << "get a new link [" << ip << "]" << std::endl;
				Service(sock,ip,port);  //孙子进程提供服务
				exit(0);
			}
			//waitpid();  //阻塞的

			//father
			close(sock);
			waitpid(id,nullptr,0);  //把爸爸回收
		}
	}
	void Service(int sock,std::string ip,int port)
	{
		char buffer[1024];
		while(true)
		{
			ssize_t size = read(sock,buffer,sizeof(buffer)-1);
			if(size > 0)
			{
				buffer[size] = 0;
				std::cout << ip <<":" << port <<"#" << buffer << std::endl;
				write(sock,buffer,size);
			}
			else if(size == 0)
			{
				std::cout << ip <<":" << port <<"close" << std::endl;
				break;
			}
			else
			{
				std::cerr << sock << "read error" << std::endl;
				break;
			}
		}
		close(sock);
		std:;cout << "serice done" << std::endl;
	}
	~TcpServer()
	{
		if(listen_sock >= 0)
			close(listen_sock);
	}
};

tcp_client.hpp

#pragma once

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

class TcpClient
{
private:
	std::string svr_ip;
	int svr_port;
	int sock;
public:
	TcpClient(std::string _ip,int _port):svr_ip(_ip),svr_port(_port),sozk(-1)
	{}
	bool InitTcpClient()
	[
		sock = socket(AF_INET,SOCK_STREAM,0);
		if(sock < 0)  //创建失败
		{
			std::cerr << "socket error" << std::endl;
			return false;
		}
		//不需要绑定,不需要监听
		return true;
	}

	//需要连接服务器
	void Start()
	{
		struct sockaddr_in peer;
		memset(&peer,0,sizeof(peer));
		peer.sin_family = AF_INET;
		peer.sin_port = htons(svr_port);
		peer.sin_addr.s_addr = inet_addr(svr_ip.c_str());
		
		if(connect(sock,(struct sockaddr*)&peer,sizeof(peer)) == 0)
		{
			//success
			std::cout << "connect success..." << std::endl;
			Request(sock);
		}
		else
		{
			//fail
			std::cout << "connect fail..." << std::endl;
		}
	}
	void Request(int sock)
	{
		std::string message;
		char buffer[1024];
		while(true)
		{
			std::cout << "Please Enter#";
			std::cin >> message;  //输入消息

			write(sock,message.c_str(),message.size());  //发过去
			ssize_t s = read(sock,buffer,sizeof(buffer)-1); //读取发送过来的数据
			if(s > 0)
			{
				buffer[s] = 0;
				std::cout << "server echo#" << buffer << std::endl;
			}
		}
	}
	~TcpClient()
	{
		if(sock >= 0)
			close(sock);
	}
};

tcp_client.cc

#include "tcp_client.hpp"

void Usage(std::string proc)
{
	std::cout << "Usage: " << proc << "server_ip server_port" << std:;endl;
}
//  3个参数 :./client  server_ip   server_port
int main(int argc, char *argv[])
{
	if(argc != 3)
	{
		Usage(argv[0]);
		return 1;
	}
	TcpClient tcli(argv[1],atoi(argv[2]));
	tcli.InitTcpClient();
	tcli.Start();
	return 0;
}

tcp_server.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>

#define BACKLOG 5  //全连接队列长度
class TcpServer
{
private:
	int port;
	int listen_sock;  //tcp是面向连接的,需要socket,监听套接字
public:
	TcpServer(int _port):port(_port),listen_sock(-1)
	{}
	bool InitTcpServer()
	{
		listen_sock = socket(AF_INET,SOCK_STREAM,0);
		if(listen_sock < 0)  //创建套接字失败 
		{
			std::cerr << "socket error" << std::endl;
			return false;
		}
		
		//绑定
		struct sockaddr_in local;
		memset(&local,0,sizeof(local));  //初始化
		local.sin_family = AE_INET;
		local.sin_port = hons(port);
		local.sin_addr.s_addr = INADDR_ANY;
		
		if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)  //绑定失败
		{
			std::cerr << "bind error" << std::endl;
			return false;
		}
		
		//有连接到来,需要监听
		if(listen(listen_sock,BACKLOG) < 0)  //监听失败
		{
			std::cerr << "listen error" << std::endl;
			return false;
		}
		return true;
	}
	void Loop()
	{ 
		//第一种方法:signal(SIGCHLD,SIG_IGN);  //会自动释放退出进程
		//获取连接,知道连接的scoket与内容
		struct sockaddr_in peer;
		for( ; ; )
		{
			socklen_t len = sizeof(peer);
			int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);  //返回值是一个文件描述符  
			if(sock < 0)
			{
				std::cerr << "accept error" << std:;endl;
				continue;  //没有获取新连接成功,则继续获取
			}
			//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
			//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
			std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;  

			//处理请求
			std::string ip = inet_ntoa(peer.sin_addr);
			int port = ntohs(peer.sin_port);
			std::cout << "get a new link [" << ip << "]" << std::endl;
			pid_t id = fork();  //爷爷  爸爸   孙子
			if(id == 0)
			{
				//child 管道
				if(fork()> 0) 
				{
					
				}
				Service(sock,ip,port);
			}
			//waitpid();  //阻塞的
		}
	}
	void Service(int sock,std::string ip,int port)
	{
		char buffer[1024];
		while(true)
		{
			ssize_t size = read(sock,buffer,sizeof(buffer)-1);
			if(size > 0)
			{
				buffer[size] = 0;
				std::cout << ip <<":" << port <<"#" << buffer << std::endl;
				write(sock,buffer,size);
			}
			else if(size == 0)
			{
				std::cout << ip <<":" << port <<"close" << std::endl;
				break;
			}
			else
			{
				std::cerr << sock << "read error" << std::endl;
				break;
			}
		}
		close(sock);
		std:;cout << "serice done" << std::endl;
	}
	~TcpServer()
	{
		if(listen_sock >= 0)
			close(listen_sock);
	}
};

tcp_server.cc

#include "tcp_server.hpp"
void Usage(std::string proc)
{
	std::cout << "Usage: " << proc << " port" << std::endl;
}
int main(int argc,char *argv[])
{
	if(argc != 2)
	{
		Usage(argv[0]);
		return 1;
	}
	//TcpServer tsvr(8081);
	TcpServer tsvr(atoi(argv[1]));   //argv[1]为程序名称
	tsvr.InitTcpServer();

	tsvr.Loop();
}

三、

static void* HandlerRequest(void* arg)
{
	int sock = *(int*)arg;
	delete (int*)arg;
	pthread_detach(pthread_self());
	Service(int sock,std::string ip,int port)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值