写了一个简单的网络通信程序。
客户端向服务端发送一串字符串,服务端计算出字符串长度返回。功能很简单
客户端和服务端通信采用protobuf
客户端每次发送的长度是一定的,包括发送的整个长度,字符串和crc校验码
服务端返回的长度也是一定的,包括客户端发送的字符串长度和crc校验码
服务端和客户端做了简单的超时控制和容错处理。
贴代码:
protobuf定义:
1 message CliPack {
2 required int32 length = 1;
3 required string data = 2;
4 required int32 crc = 3;
5 }
6
7 message SvrPack {
8 required int32 client_data_length = 1;
9 required int32 crc = 2;
10 }
客户端代码:
client.h
#ifndef CLIENT_H_
#define CLIENT_H_
#include <string>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <error.h>
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include "package.pb.h"
#include "common.h"
using namespace std;
const unsigned int kServerPort = 9876;
const string kIp = "10.11.18.144";
const unsigned int kTcpBufLen = 64*1024*1024;
const unsigned int kSendBufLen = 1152;
const unsigned int kRecvBufLen = 128;
const unsigned int kRetryCount = 5;
const unsigned int kTimeOut = 50;
const unsigned int kDataMaxLength = 1024;
class Client {
public:
//explicit Client():ip_(kIp), port_(kServerPort);
explicit Client();
explicit Client(const string& ip, unsigned int port, unsigned int timeout);
virtual ~Client();
const bool get_connected() {
return connected_;
}
void set_connected(bool connected) {
connected_ = connected;
}
const unsigned int get_port() {
return port_;
}
void set_port(unsigned port) {
port = port_;
}
const string get_ip() {
return ip_;
}
void set_ip(const string& ip) {
ip_ = ip;
}
public:
int Request(CliPack& pack);
private:
explicit Client(const Client&) {}
Client& operator = (const Client&) {}
private:
bool CheckConnect();
bool Send(const char* buf, int len);
bool Recv();
void SetSocketOpt();
int GetData(const char* buf);
private:
string ip_;
unsigned int port_;
int sockfd_;
bool connected_;
int time_out_;
int length_;
};
#endif
client.cc
#include "client.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <error.h>
Client::Client() {
ip_ = kIp;
port_ = kServerPort;
time_out_ = kTimeOut;
connected_ = false;
sockfd_ = -1;
length_ = -1;
}
Client::Client(const string& ip, unsigned int port, unsigned int timeout) {
ip_ = ip;
port_ = port;
time_out_ = timeout;
connected_ = false;
sockfd_ = -1;
length_ = -1;
}
Client::~Client() {
close(sockfd_);
}
bool Client::CheckConnect() {
if (connected_) {
return true;
}
if (-1 != sockfd_) {
close(sockfd_);
}
sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
SetSocketOpt();
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip_.c_str());
addr.sin_port = htons(port_);
int res = connect(sockfd_, (sockaddr*)&addr, sizeof(addr));
if (0 == res) {
connected_ = true;
return true;
} else if (res < 0 && errno != EINPROGRESS) {
printf("Client::CheckConnect --> err:%s\n", strerror(errno));
return false;
}
struct pollfd fds;
fds.fd = sockfd_;
fds.events = POLLOUT;
res = poll(&fds, 1, time_out_);
if (1 == res) {
connected_ = true;
//printf("haha3\n");
return true;
}
return false;
}
void Client::SetSocketOpt() {
int res = setsockopt(sockfd_, SOL_SOCKET, SO_RCVBUF, &kTcpBufLen, sizeof(kTcpBufLen));
if (res != 0) {
printf("Client::SetSocketOpt --> set recvbuf err, %s\n", strerror(errno));
return;
}
res = setsockopt(sockfd_, SOL_SOCKET, SO_SNDBUF, &kTcpBufLen, sizeof(kTcpBufLen));
if (res != 0) {
printf("Client::SetSocketOpt --> set sendbuf err, %s\n", strerror(errno));
return;
}
int flags = fcntl(sockfd_, F_GETFL, 0);
res = fcntl(sockfd_, F_SETFL, flags | O_NONBLOCK);
if (res < 0) {
printf("Client::SetSocketOpt --> set fcntl err, %s\n", strerror(errno));
return;
}
struct linger l;
l.l_onoff = 1;
l.l_linger = 0;
res = setsockopt(sockfd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
if (res != 0) {
printf("Client::SetSocketOpt --> set linger err, %s\n", strerror(errno));
return;
}
int nodelay = 1;
res = setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
if (res != 0) {
printf("Client::SetSocketOpt --> set nodelay err, %s\n", strerror(errno));
return;
}
}
bool Client::Send(const char* buf, int len) {
if (buf == NULL || len <= 0)
return false;
int tmp = 0;
int count = 0;
while (count < kRetryCount) {
int res = send(sockfd_, buf+tmp, len-tmp, 0);
if (res < 0) {
printf("Client::Send --> err, %d,%s,%d\n", errno, ip_.c_str(), port_);
return false;
}
tmp += res;
if (tmp == len) {
return true;
}
struct pollfd fds;
fds.fd = sockfd_;
fds.events = POLLOUT;
res = poll(&fds, 1, time_out_);
if (res != 1) {
printf("Client::Send --> %d, %s\n", res, strerror(errno));
return false;
}
count++;
}
return false;
}
bool Client::Recv() {
int pos = 0;
char recv_buf[kRecvBufLen];
memset(recv_buf, 0, kRecvBufLen);
while (1) {
struct pollfd fds;
fds.fd = sockfd_;
fds.events = POLLIN;
int res = poll(&fds, 1, time_out_);
if (res <= 0) {
if (res < 0) {
printf("Recv poll-->error res:%d\n", res);
} else {
printf("Recv poll-->time out res:%d\n", res);
}
return false;
} else if (1 == res) {
int tmp = recv(sockfd_, recv_buf + pos, kRecvBufLen - pos, 0);
if (-1 == tmp) {
if (errno != EAGAIN) {
printf("Recv recv error tmp = -1\n");
return false;
} else {
continue;
}
} else if (0 == tmp) {
printf("Recv recv error tmp = 0\n");
return false;
} else {
if (kRecvBufLen - pos == tmp) {
int data = GetData(recv_buf);
if (data < 0) {
printf("Recv data error data:%d\n", data);
} else {
printf("Recv data length data length:%d\n", data);
}
return true;
} else {
pos = pos + tmp;
continue;
}
}
} else {
printf("Recv poll error res > 1\n");
return false;
}
}
return false;
}
int Client::GetData(const char* buf) {
if (buf == NULL)
return -1;
SvrPack pack;
pack.ParseFromString(buf);
int length = pack.client_data_length();
int crc = pack.crc();
stringstream ss;
ss << length;
int compute_crc = Common::GetCrc32(ss.str().c_str(), strlen(ss.str().c_str()));
if (compute_crc == crc) {
if (length > 0)
return length;
else
return -2;
}
return -3;
}
int Client::Request(CliPack& pack) {
if (!CheckConnect()) {
close(sockfd_);
connected_ = false;
printf("Request CheckConnect error \n");
return -1;
}
char send_buf[kSendBufLen];
memset(send_buf, 0, kSendBufLen);
string str;
pack.SerializeToString(&str);
const char* tmp = str.c_str();
memcpy(send_buf, tmp, kSendBufLen);
if (!Send(send_buf, kSendBufLen)) {
close(sockfd_);
connected_ = false;
printf("Request Send error \n");
return -2;
}
if (!Recv()) {
close(sockfd_);
connected_ = false;
printf("Request Recv error \n");
return -3;
}
return 0;
}
main.cc:
#include "client.h"
#include "common.h"
using namespace std;
int main(int argc, char* argv[]) {
CliPack pack;
char* data = new char[kSendBufLen];
memset(data, 0, kSendBufLen);
memcpy(data, "123456", kSendBufLen);
pack.set_length(kSendBufLen);
pack.set_data(data);
int crc = Common::GetCrc32(data, strlen(data));
//printf("crc->%d\n", crc);
pack.set_crc(crc);
Client* c = new Client();
c->Request(pack);
delete [] data;
return 0;
}
服务端代码:
server.h
#ifndef _SERVER_H_
#define _SERVER_H_
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include "common.h"
#include "package.pb.h"
const std::string kServerIp = "10.11.18.144";
const unsigned int kServerPort = 9876;
const unsigned int kTcpBufLen = 64*1024*1024;
const unsigned int kListenQueueSize = 1024;
const unsigned int kRecvBufferLen = 1152;
const unsigned int kSendBufferLen = 128;
const unsigned int kTimeOut = 50;
const unsigned int kRetryCount = 5;
class Server {
public:
explicit Server() {
server_ip_ = kServerIp;
server_port_ = kServerPort;
time_out_ = kTimeOut;
server_sockfd_ = -1;
}
virtual ~Server() {}
private:
explicit Server(const Server&);
Server& operator = (const Server&);
public:
std::string const get_server_ip() const;
void set_server_ip(std::string server_ip);
unsigned int const get_server_port() const;
void set_server_port(unsigned int server_port);
void set_time_out(int timeout);
int const get_time_out() const;
public:
virtual void Run() = 0;
protected:
virtual int Init();
virtual void SetSocketOpt();
virtual int Send(const char* buf, int len, int fd);
virtual int GetData(const char* buf);
protected:
std::string server_ip_;
unsigned int server_port_;
int server_sockfd_;
int time_out_;
};
class SimpleServer : public Server {
public:
explicit SimpleServer() {}
~SimpleServer() {}
private:
explicit SimpleServer(const SimpleServer&);
SimpleServer& operator = (const SimpleServer&);
private:
int Process();
public:
void Run();
};
class ForkServer: public Server {
public:
explicit ForkServer() {}
~ForkServer() {}
private:
explicit ForkServer(const ForkServer&);
ForkServer& operator = (const ForkServer&);
private:
int Process();
public:
void Run();
};
#endif
server.cc
#include "server.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
unsigned int const Server::get_server_port() const {
return server_port_;
}
void Server::set_server_port(unsigned int server_port) {
server_port_ = server_port;
}
std::string const Server::get_server_ip() const {
return server_ip_;
}
void Server::set_server_ip(std::string server_ip) {
server_ip_ = server_ip;
}
void Server::set_time_out(int timeout) {
time_out_ = timeout;
}
int const Server::get_time_out() const {
return time_out_;
}
void Server::SetSocketOpt() {
int res = setsockopt(server_sockfd_, SOL_SOCKET, SO_RCVBUF, &kTcpBufLen, sizeof(kTcpBufLen));
if (res != 0) {
printf("Server::SetSocketOpt --> set recvbuf err, %s\n", strerror(errno));
return;
}
res = setsockopt(server_sockfd_, SOL_SOCKET, SO_SNDBUF, &kTcpBufLen, sizeof(kTcpBufLen));
if (res != 0) {
printf("Server::SetSocketOpt --> set sendbuf err, %s\n", strerror(errno));
return;
}
int option = 1;
setsockopt (server_sockfd_, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) ;
int flags = fcntl(server_sockfd_, F_GETFL, 0);
res = fcntl(server_sockfd_, F_SETFL, flags | O_NONBLOCK);
if (res < 0) {
printf("Server::SetSocketOpt --> set fcntl err, %s\n", strerror(errno));
return;
}
struct linger l;
l.l_onoff = 1;
l.l_linger = 0;
res = setsockopt(server_sockfd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
if (res != 0) {
printf("Server::SetSocketOpt --> set linger err, %s\n", strerror(errno));
return;
}
int nodelay = 1;
res = setsockopt(server_sockfd_, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
if (res != 0) {
printf("Server::SetSocketOpt --> set nodelay err, %s\n", strerror(errno));
return;
}
}
int Server::Send(const char* buf, int len, int fd) {
if (buf == NULL || len <= 0)
return -1;
if (-1 == fd) {
return -1;
}
int tmp = 0;
int count = 0;
while (count < kRetryCount) {
int res = send(fd, buf+tmp, len-tmp, 0);
if (res < 0) {
printf("Server::Send --> err, %d,%s,%d\n", errno, server_ip_.c_str(), server_port_);
return -1;
}
tmp += res;
if (tmp == len) {
return 0;
}
struct pollfd fds;
fds.fd = fd;
fds.events = POLLOUT;
res = poll(&fds, 1, time_out_);
if (res != 1) {
printf("Server::Send --> %d, %s\n", res, strerror(errno));
return -1;
}
count++;
}
return -1;
}
int Server::Init() {
server_sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == server_sockfd_) {
printf("create socket error: %s(errno, %d)\n", strerror(errno), errno);
return -1;
}
SetSocketOpt();
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr(kServerIp.c_str());
server_address.sin_port = htons(kServerPort);
if (-1 == bind(server_sockfd_, (struct sockaddr*)&server_address, sizeof(struct sockaddr))) {
printf("bind socket error: %s(errno, %d)\n", strerror(errno), errno);
close(server_sockfd_);
return -1;
}
if (-1 == listen(server_sockfd_, kListenQueueSize)) {
printf("listen socket error: %s(errno, %d)\n", strerror(errno), errno);
close(server_sockfd_);
return -1;
}
return 0;
}
int Server::GetData(const char* buf) {
if (NULL == buf)
return -1;
CliPack pack;
pack.ParseFromString(buf);
int length = pack.length();
int crc = pack.crc();
char* data = new char[kRecvBufferLen];
memset(data, 0, kRecvBufferLen);
memcpy(data, pack.data().c_str(), kRecvBufferLen);
int compute_crc = Common::GetCrc32(data, strlen(data));
delete [] data;
if (compute_crc != crc)
return -1;
if (pack.data().c_str() == NULL)
return -2;
return strlen(pack.data().c_str());
}
int SimpleServer::Process() {
while (1) {
int conn_fd = -1;
conn_fd = accept(server_sockfd_, (struct sockaddr*)NULL, NULL);
if (-1 == conn_fd) {
if (errno == EINTR || errno == EAGAIN) {
continue;
} else {
printf("Recv accept socket error: %s(errno, %d)\n", strerror(errno), errno);
continue;
}
}
char buf[kRecvBufferLen];
memset(buf, 0, kRecvBufferLen);
int pos = 0;
while (1) {
struct pollfd fds;
fds.fd = conn_fd;
fds.events = POLLIN;
int res = poll(&fds, 1, time_out_);
if (res <= 0) {
if (res < 0) {
printf("Process poll-->error res:%d\n", res);
} else {
printf("Process poll-->time out res:%d\n", res);
}
close(conn_fd);
break;
} else if (1 == res) {
int tmp = recv(conn_fd, buf + pos, kRecvBufferLen - pos, 0);
if (-1 == tmp) {
if (errno != EAGAIN) {
printf("Process recv error tmp = -1\n");
close(conn_fd);
break;
} else {
continue;
}
} else if (0 == tmp) {
close(conn_fd);
printf("Process recv error tmp = 0\n");
break;
} else {
if (kRecvBufferLen - pos == tmp) {
int data = GetData(buf);
if (data < 0) {
printf("Process GetData error data = %d\n", data);
close(conn_fd);
} else {
char send_buf[kSendBufferLen];
SvrPack pack;
memset(send_buf, 0, kSendBufferLen);
pack.set_client_data_length(data);
stringstream ss;
ss << data;
int crc = Common::GetCrc32(ss.str().c_str(), strlen(ss.str().c_str()));
pack.set_crc(crc);
string str;
pack.SerializeToString(&str);
memcpy(send_buf, str.c_str(), kSendBufferLen);
if (-1 == Send(send_buf, kSendBufferLen, conn_fd)) {
printf("Process Send error\n");
close(conn_fd);
}
}
break;
} else {
pos = pos + tmp;
continue;
}
}
} else {
close(conn_fd);
printf("Process poll error res > 1\n");
break;
}
}
}
close(server_sockfd_);
return 0;
}
void SimpleServer::Run() {
if (-1 == Init()) {
printf("SimpleServer::Run Init error\n");
return;
}
if (-1 == Process()) {
printf("SimpleServer::Run Process error\n");
return;
}
}
int ForkServer::Process() {
while (1) {
int conn_fd = -1;
conn_fd = accept(server_sockfd_, (struct sockaddr*)NULL, NULL);
if (-1 == conn_fd) {
if (errno == EINTR || errno == EAGAIN) {
continue;
} else {
printf("Recv accept socket error: %s(errno, %d)\n", strerror(errno), errno);
continue;
}
}
pid_t pid;
pid = fork();
if (0 == pid) {
char buf[kRecvBufferLen];
memset(buf, 0, kRecvBufferLen);
int pos = 0;
while (1) {
struct pollfd fds;
fds.fd = conn_fd;
fds.events = POLLIN;
int res = poll(&fds, 1, time_out_);
if (res <= 0) {
if (res < 0) {
printf("Process poll-->error res:%d\n", res);
} else {
printf("Process poll-->time out res:%d\n", res);
}
close(conn_fd);
exit(0);
} else if (1 == res) {
int tmp = recv(conn_fd, buf + pos, kRecvBufferLen - pos, 0);
if (-1 == tmp) {
if (errno != EAGAIN) {
printf("Process recv error tmp = -1\n");
close(conn_fd);
exit(0);
} else {
continue;
}
} else if (0 == tmp) {
close(conn_fd);
printf("Process recv error tmp = 0\n");
exit(0);
} else {
if (kRecvBufferLen - pos == tmp) {
int data = GetData(buf);
if (data < 0) {
printf("Process GetData error data = %d\n", data);
close(conn_fd);
} else {
char send_buf[kSendBufferLen];
SvrPack pack;
memset(send_buf, 0, kSendBufferLen);
pack.set_client_data_length(data);
stringstream ss;
ss << data;
int crc = Common::GetCrc32(ss.str().c_str(), strlen(ss.str().c_str()));
pack.set_crc(crc);
string str;
pack.SerializeToString(&str);
memcpy(send_buf, str.c_str(), kSendBufferLen);
if (-1 == Send(send_buf, kSendBufferLen, conn_fd)) {
printf("Process Send error\n");
close(conn_fd);
}
}
exit(0);
} else {
pos = pos + tmp;
continue;
}
}
} else {
close(conn_fd);
printf("Process poll error res > 1\n");
exit(0);
}
}
} else {
close(conn_fd);
}
}
close(server_sockfd_);
return 0;
}
void ForkServer::Run() {
if (-1 == Init()) {
printf("SimpleServer::Run Init error\n");
return;
}
if (-1 == Process()) {
printf("SimpleServer::Run Process error\n");
return;
}
}
main.cc
#include "server.h"
int main(int argc, char*argv[]) {
//Server* server = new SimpleServer();
//server->Run();
Server* server = new ForkServer();
server->Run();
}
整个代码没有进行过充分测试,超时时间设置也有些问题,有些重复代码封装的也不太好,不过就先这样吧
有趣有爱有价值:http://www.qihu100.com