MySocket基类:
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
#include <string>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/tcp.h>
using namespace std;
class MySocket{
public:
MySocket();
MySocket(int fd);
int send(const char* buf, int len);
int recv(char* buf, int len);
void close();
bool set_non_blocking(); //设置读写非阻塞
bool set_send_buffer(int size); //设置socket的发送缓冲区
bool set_recv_buffer(int size); //设置socket的接收缓冲区
bool set_linger(bool active, int seconds); //若还有数据未发送,socket等待一段时间再关闭
bool set_keepalive(); //是否发送心跳包
bool set_reuseaddr(); //设置端口复用
int fd();
protected:
int m_sockfd = 0; //连接描述符
const char *m_host = "";
unsigned int m_port = 0;
};
#include <mysocket.h>
MySocket::MySocket(){
m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(m_sockfd < 0){
perror("socket error:");
}
}
MySocket::MySocket(int fd){
m_sockfd = fd;
}
int MySocket::send(const char* buf, int len){
int slen = ::send(m_sockfd, buf, len, 0);
if(slen < 0){
perror("send");
}
return slen;
}
int MySocket::recv(char* buf, int len){
int rlen = ::recv(m_sockfd, buf, len, 0);
if(rlen < 0){
perror("recv");
}
return rlen;
}
void MySocket::close(){
::close(m_sockfd);
}
int MySocket::fd(){
return m_sockfd;
}
bool MySocket::set_non_blocking(){
int flags = fcntl(m_sockfd, F_GETFL, 0);
if(flags<0){
printf("socket et_non_blocking error: errno=%d, errmsg=%s", errno, strerror(errno));
}
flags |= O_NONBLOCK;
if(fcntl(m_sockfd, F_SETFL, flags)<0){
printf("socket set_non_blocking error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
return true;
}
bool MySocket::set_send_buffer(int size){
int buff_size=size;
if(setsockopt(m_sockfd, SOL_SOCKET, SO_SNDBUF, &buff_size, sizeof(buff_size)) < 0){
printf("socket set_send_buffer error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
return true;
}
bool MySocket::set_recv_buffer(int size){
int buff_size=size;
if(setsockopt(m_sockfd, SOL_SOCKET, SO_RCVBUF, &buff_size, sizeof(buff_size)) < 0){
printf("socket set_send_buffer error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
return true;
}
bool MySocket::set_linger(bool active, int seconds){
struct linger l;
memset(&l, 0, sizeof(l));
l.l_onoff = active?1:0; //开启或关闭linger
l.l_linger = seconds; //linger的时间
if(setsockopt(m_sockfd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0){
printf("socket set_linger error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
return true;
}
bool MySocket::set_keepalive(){
int flag = 1; //开启心跳包模式
if(setsockopt(m_sockfd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0){
printf("socket set_keepalive error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
int idle = 10; //10秒无数据,发送心跳包
if(setsockopt(m_sockfd, SOL_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)) < 0){
printf("socket set_tcpidle error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
int intv = 5; //无回应,5秒后发送心跳包
if(setsockopt(m_sockfd, SOL_TCP, TCP_KEEPINTVL, &intv, sizeof(intv)) < 0){
printf("socket set_tcpintv error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
int cnt =3; //连续3次没有收到回应,关闭连接
if(setsockopt(m_sockfd, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)) < 0){
printf("socket set_tcpcnt error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
return true;
}
bool MySocket::set_reuseaddr(){
int flag = 1;
if(setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0){
printf("socket set_reuseaddr error: errno=%d, errmsg=%s", errno, strerror(errno));
return false;
}
return true;
}
SO_REUSEADDR可以用在以下四种情况下:
1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。
3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。
4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。
需要注意的是,设置端口复用函数要在绑定之前调用,而且只要绑定到同一个端口的所有套接字都得设置复用
ClientSocket类:
#include <mysocket.h>
class ClientSocket : public MySocket{
public:
ClientSocket() : MySocket(){}
ClientSocket(int sockfd) : MySocket(sockfd){}
bool connect(char *ip, unsigned int port);
};
#include <clientsocket.h>
bool ClientSocket::connect(char *ip, unsigned int port){
struct sockaddr_in sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = inet_addr(ip);
sockaddr.sin_port = htons(port);
if(::connect(m_sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0){
perror("connect");
return false;
}
else{
m_host = ip;
m_port = port;
return true;
}
}
ServerSocket类:
#include <mysocket.h>
class ServerSocket : public MySocket{
public:
ServerSocket() : MySocket(){}
ServerSocket(int sockfd) : MySocket(sockfd){}
bool bind(const char *host, const int port);
bool listen(int backlog = 128);
int accept();
};
#include <serversocket.h>
bool ServerSocket::bind(const char *host, const int port){
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = inet_addr(host);
sockaddr.sin_port = htons(port);
if(::bind(m_sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0){
perror("bind");
return false;
}
else{
m_host = (char*)host;
m_port = port;
return true;
}
}
bool ServerSocket::listen(int backlog){
if(::listen(m_sockfd, backlog) < 0){
perror("listen");
return false;
}
else{
return true;
}
}
int ServerSocket::accept(){
int cfd = ::accept(m_sockfd, nullptr, nullptr);
if(cfd < 0){
perror("accept");
}
return cfd;
}
客户端程序:
#include <clientsocket.h>
#include <iostream>
using namespace std;
int main(){
ClientSocket csocket;
if(csocket.fd() < 0){
exit(1);
}
char host[] = "127.0.0.1";
int port = 9998;
if(!csocket.connect(host, port)){
csocket.close();
exit(1);
}
char sendbuf[] = "This is message from client";
if(csocket.send(sendbuf, sizeof(sendbuf)) < 0){
csocket.close();
exit(1);
}
char recvbuf[1024];
if(csocket.recv(recvbuf, sizeof(recvbuf)) < 0){
csocket.close();
exit(1);
}
cout << recvbuf <<endl;
csocket.close();
return 0;
}
服务端程序:
#include <serversocket.h>
#include <clientsocket.h>
#include <iostream>
using namespace std;
int main(){
ServerSocket ssocket;
if(ssocket.fd() < 0){
exit(1);
}
char host[] = "127.0.0.1";
int port = 9998;
if(!ssocket.bind(host, port)){
ssocket.close();
exit(1);
}
if(!ssocket.listen(128)){
ssocket.close();
exit(1);
}
while(true){
int cfd = ssocket.accept();
ClientSocket csocket(cfd);
char recvbuf[1024];
if(csocket.recv(recvbuf, sizeof(recvbuf)) < 0){
csocket.close();
continue;
}
cout << "Recieve: clientfd = " << cfd <<", message = " << recvbuf << endl;
char sendbuf[] = "Server recieved your message";
if(csocket.send(sendbuf, sizeof(sendbuf)) < 0){
csocket.close();
continue;
}
csocket.close();
}
ssocket.close();
return 0;
}