【Linux】基于TCP协议实现网络通信
基于TCP协议实现网络通信
tcp_com.h
#ifndef __M_TCP_H__
#define __M_TCP_H__
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define CHECK_RET(q) if(q == false){return -1;}
class TcpSocket
{
private:
int _sockfd;
public:
TcpSocket():_sockfd(-1){}
//1.创建socket
bool Socket()
{
_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(_sockfd < 0)
{
perror("socket error");
return false;
}
return true;
}
//2.绑定地址信息
bool Bind(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());
socklen_t len = sizeof(sockaddr_in);
int ret = bind(_sockfd,(sockaddr*)&addr,len);
if(ret < 0)
{
perror("bind error");
return false;
}
return true;
}
//3.服务端-开始监听
bool Listen(int backlog = 5)
{
//int listen(int sockfd, int backlog);
//sockfd:套接字描述符
//backlog:同一时间的最大并发连接数,即同一时间能够同时接收的客户端连接请求数
//返回值:成功:0 失败: -1
int ret = listen(_sockfd,backlog);
if(ret < 0)
{
perror("listen error");
return -1;
}
return true;
}
//4.服务端-获取连接成功的socket描述符
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//sockfd: 套接字描述符
//addr: 新连接的客户端地址信息
//addrlen: 地址信息长度(输入输出复合参数)
//返回值: 成功:新建立的socket的描述符 失败: -1
bool Accept(TcpSocket *sock,sockaddr_in *_addr = NULL)
{
int newfd;
sockaddr_in addr;
socklen_t len = sizeof(sockaddr_in);
newfd = accept(_sockfd,(sockaddr*)&addr,&len);
if(newfd < 0)
{
perror("accept error");
return false;
}
sock->_sockfd = newfd;
if(_addr)
{
memcpy((void*)_addr,(void*)&addr,len);
}
return true;
}
//5.服务端-收发数据
ssize_t Recv(char *buf,size_t len)
{
//ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//返回值:成功;实际的接收长度 失败:-1
ssize_t ret = recv(_sockfd,buf,len,0);
return ret;
}
ssize_t Send(char *buf,size_t len)
{
//ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//返回值:成功;实际的接收长度 失败:-1
int slen = 0;
while(slen != len)
{
ssize_t s = send(_sockfd,buf + slen,len - slen,0);
if(s < 0)
{
//EINTR A signal occurred before any data was transmitted;
//EAGAIN or EWOULDBLOCK
//The socket is marked non-blocking and the requested operation would block.POSIX.1-2001 allows either error to be
//returned for this case, and does not require these constants to have the same value, so a portable application should
//check for both possibilities.
//非阻塞才会出现 EAGAIN 这种错误
if(errno == EINTR || errno == EAGAIN)
{
continue;
}
return -1;
}
slen += s;
}
return slen;
}
//3.客户端-发起连接请求
bool Connect(std::string &ip,uint16_t port)
{
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//sockfd: 套接字描述符
//addr: 服务端地址信息
//addrlen: 地址信息长度
//返回值: 成功: 0 失败: -1
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t len = sizeof(sockaddr_in);
int ret = connect(_sockfd,(sockaddr*)&addr,len);
if(ret < 0)
{
perror("connect error");
return false;
}
return true;
}
//6.关闭socket
bool Close()
{
if(_sockfd >= 0)
{
close(_sockfd);
_sockfd = -1;
}
return true;
}
};
#endif
tcp_cli.cpp
#include "tcp_com.h"
class TcpClient
{
private:
TcpSocket sock;
public:
bool Start(std::string &ip,uint16_t port)
{
CHECK_RET(sock.Socket());
CHECK_RET(sock.Connect(ip,port));
while(1)
{
char buff[1024] = {0};
scanf("%s",buff);
sock.Send(buff,strlen(buff));
memset(buff,0x00,1024);
sock.Recv(buff,1023);
printf("server say:%s\n",buff);
}
CHECK_RET(sock.Close());
return true;
}
};
int main(int argc,char *argv[])
{
if(argc != 3)
{
printf("usage: ./tcp_cli ip port\n");
return -1;
}
uint16_t port = atoi(argv[2]);
std::string ip = argv[1];
TcpClient client;
client.Start(ip,port);
return 0;
}
服务端实现 tcp_sev.cpp
#include "tcp_com.h"
#include <pthread.h>
class TcpServer
{
private:
TcpSocket sock;
public:
static void *thr_start(void *arg)
{
TcpSocket *cli_sock = (TcpSocket*)arg;
while(1)
{
char buff[1024] = {0};
if(cli_sock->Recv(buff,1023) < 0)
{
break;
}
printf("client say:%s\n",buff);
memset(buff,0x00,1024);
scanf("%s",buff);
if(cli_sock->Send(buff,strlen(buff)) < 0)
{
break;
}
}
cli_sock->Close();
delete cli_sock;
return NULL;
}
bool Worker(TcpSocket *sock)
{
pthread_t tid;
int ret = pthread_create(&tid,NULL,thr_start,(void*)sock);
if(ret != 0)
{
printf("create worker error\n");
return false;
}
pthread_detach(tid);
return true;
}
bool Start(std::string &ip,uint16_t port)
{
CHECK_RET(sock.Socket());
CHECK_RET(sock.Bind(ip,port));
CHECK_RET(sock.Listen());
while(1)
{
TcpSocket *cli_sock = new TcpSocket();
CHECK_RET(sock.Accept(cli_sock));
Worker(cli_sock);
}
CHECK_RET(sock.Close());
}
};
int main(int argc,char *argv[])
{
if(argc != 3)
{
printf("usage: ./tcp_sev ip port\n");
return -1;
}
uint16_t port = atoi(argv[2]);
std::string ip = argv[1];
TcpServer server;
server.Start(ip,port);
return 0;
}