Linux下编写C++服务器(简单Socket客户端服务器)

安装好环境之后,我们就可以写服务器了,今天先写一个简单的socket服务器。
先给出Linux编码风格,尽量按着这些点写。

头文件

我们在上一次的test项目下,新增一个名字叫MySocket的类,由于windows和linux需要的头文件不同,我们需要查找一下Socket需要的头文件,添加上去后显示无法打开源文件,是因为VS只有编译时才会连接Linux虚拟机的头文件。
在这里插入图片描述
这时我们需要把linux上的头文件复制到vs的linux header path,网上的说法是头文件在**/usr/include、/usr/local/include**,但我的后者是空的,所以只复制前者,输入

cp -r /usr/include /mnt/hgfs/LinuxShare

在这里插入图片描述
复制该文件到共享文件夹后,把共享目录放到VS包含目录内,我的共享目录是(E:\LinuxShare\include),其中子目录(E:\LinuxShare\include\c++\xxx)需要单独包含进去,所以我们需要在如下位置末尾添加;E:\LinuxShare\include;E:\LinuxShare\include\c++\4.8.2;
在这里插入图片描述
在这里插入图片描述
添加完后点击应用->确定,这时再看项目头文件,已经没有了报错。
在这里插入图片描述

封装socket

之前用socket比较多,但Linux下socket的函数有一点不同,为了方便以后使用,这里简单地封装一下,客户端和服务端都封在一起了,代码如下:
MySocket.h

#pragma once
  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h> 
#include <unistd.h> 

class MySocket
{
public:
	MySocket();
	~MySocket();
	
protected:
	int socket_fd;
	struct sockaddr_in server_addr;
	char err_msg[256];

public:
	//初始化socket环境,只调用一次
	bool init_socket();
	//获取错误信息
	char* get_err_msg();


};

class MySocketServer :public MySocket
{
private:
	int recvbytes, res, flags;
	int client_fd;
public:
	bool create_server(unsigned int port);

	bool accept_client(int &client_socket);
	// 接收数据
	// 出参:buffer 缓存区
	// 入参:bufferLen 缓存区大小
	// 出参:recvLen 已接收数据的大小
	bool recv_data(char * buffer, int buffer_len, int& recv_len);

	bool send_data(const char * data, int len);

	void close_client(int client_socket);

	void close_server();
};



class MySocketClient :public MySocket
{
public:
	bool client_connect(const char* ip, unsigned int port);
	// 接收数据
	// 出参:buffer 缓存区
	// 入参:bufferLen 缓存区大小
	// 出参:recvLen 已接收数据的大小
	bool recv_data(char * buffer, int buffer_len, int& recv_len);

	bool send_data(const char * data, int len);

	void client_close();
};

MySocket.cpp

#include "MySocket.h"
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h> 
#include<errno.h> 
#include<fcntl.h>
#include <arpa/inet.h>

MySocket::MySocket()
{
}


MySocket::~MySocket()
{
}

bool MySocket::init_socket()
{
	if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < -1) {
		snprintf(err_msg, sizeof(err_msg), "create socket error:%s(srrno:%d)\n",strerror(errno),errno);
	}
	return true;
}

char* MySocket::get_err_msg()
{
	return err_msg;
}

bool MySocketServer::create_server(unsigned int port)
{
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
	server_addr.sin_port = htons(port);
	flags = fcntl(socket_fd, F_GETFL, 0);
	fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);//设置为非阻塞
	//设置超时
	struct timeval timeout = { 3,0 };//3s
	if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)) != 0){
		snprintf(err_msg, sizeof(err_msg), "set send timeout failed: %s(errno: %d)\n", strerror(errno), errno);
		return false;
	}
	if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)) != 0) {
		snprintf(err_msg, sizeof(err_msg), "set recv timeout failed: %s(errno: %d)\n", strerror(errno), errno);
		return false;
	}
	//设置重用地址,防止Address already in use
	int on = 1;
	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	//将本地地址绑定到所创建的套接字上  
	if (bind(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
		snprintf(err_msg, sizeof(err_msg), "bind socket error: %s(errno: %d)\n", strerror(errno), errno);
		return false;
	}
	//开始监听是否有客户端连接  
	if (listen(socket_fd, 10) == -1) {
		snprintf(err_msg, sizeof(err_msg), "listen socket error: %s(errno: %d)\n", strerror(errno), errno);
		return false;
	}
	return true;
}

bool MySocketServer::accept_client(int & client_socket)
{
	struct sockaddr_in client_addr;
	socklen_t client_len;
	client_socket = accept(socket_fd, (struct sockaddr*)&client_addr, &client_len);
	if (client_socket < 0)
	{
		snprintf(err_msg, sizeof(err_msg), "accept socket error: %s(errno: %d)", strerror(errno), errno);
		return false;
	}
	client_fd = client_socket;
	return true;
}

bool MySocketServer::recv_data(char * buffer, int buffer_len, int & recv_len)
{
	memset(buffer, 0, buffer_len);
	recv_len = recv(client_fd, buffer, buffer_len, 0);
	if (recv_len <= 0){
		snprintf(err_msg, sizeof(err_msg), "recv data error: %s(errno: %d)", strerror(errno), errno);
		return false;
	}
	printf("server recv:%s\n", buffer);
	return true;
}

bool MySocketServer::send_data(const char * data, int len)
{
	if (send(client_fd, data, len, 0) == -1) {
		snprintf(err_msg, sizeof(err_msg), "send data error: %s(errno: %d)", strerror(errno), errno);
		return false;
	}
		
	return true;
}

void MySocketServer::close_client(int client_socket)
{
	close(client_socket);
}

void MySocketServer::close_server()
{
	close(socket_fd);
}

bool MySocketClient::client_connect(const char * ip, unsigned int port)
{
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0) {
		snprintf(err_msg, sizeof(err_msg), "inet_pton error for %s\n", ip);
		return false;
	}

	if (connect(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
		snprintf(err_msg, sizeof(err_msg), "connect error: %s(errno: %d)\n", strerror(errno), errno);
		return false;
	}
	return true;
}

bool MySocketClient::recv_data(char * buffer, int buffer_len, int & recv_len)
{
	memset(buffer, 0, buffer_len);
	if ((recv_len = recv(socket_fd, buffer, buffer_len, 0)) == -1) {
		snprintf(err_msg, sizeof(err_msg), "recv data error: %s(errno: %d)", strerror(errno), errno);
		return false;
	}
	buffer[buffer_len] = '\0';
	printf("client recv:%s\n", buffer);
	return true;
}

bool MySocketClient::send_data(const char * data, int len)
{
	if (send(socket_fd, data, len, 0) < 0) {
		snprintf(err_msg, sizeof(err_msg), "send data error: %s(errno: %d)", strerror(errno), errno);
		return false;
	}
	return true;
}

void MySocketClient::client_close()
{
	close(socket_fd);
}

调用

main.cpp

#include<iostream>
#include<string.h>
#include<stdio.h>  
#include<stdlib.h>  
#include <pthread.h>
#include"MySocket.h"
using namespace std;

bool b_end = false;
void *serverthread(void *arg)
{
	char err_msg[256], buffer[512];
	int client_socket, recv_len;
	MySocketServer my_socket_server;
	
	if (!my_socket_server.init_socket()) {
		//strncpy(err_msg, my_socket_server.get_err_msg(), sizeof(err_msg) - 1);
		printf(my_socket_server.get_err_msg());
		return 0;
	}
	if (!my_socket_server.create_server(5000)) {
		printf(my_socket_server.get_err_msg());
		my_socket_server.close_server();
		return 0;
	}

	while (!b_end) {
		if (!my_socket_server.accept_client(client_socket)) {
			continue;
		}

		while (!b_end) {
			if (!my_socket_server.recv_data(buffer, sizeof(buffer), recv_len))
			{
				printf(my_socket_server.get_err_msg());
				my_socket_server.close_client(client_socket);
				my_socket_server.close_server();
				break;
			}
			sleep(1);
			my_socket_server.send_data(strcat(buffer, "too"), recv_len + 3);
		}
		my_socket_server.close_client(client_socket);
	}
	my_socket_server.close_server();
	printf("server exit");
	return 0;
}

void *clientthread(void *arg)
{
	char err_msg[256], buffer[512];
	int client_socket, recv_len;
	MySocketClient my_socket_client;
	if (!my_socket_client.init_socket()) {
		printf(my_socket_client.get_err_msg());
		return 0;
	}
	if (!my_socket_client.client_connect("127.0.0.1", 5000))
	{
		printf(my_socket_client.get_err_msg());
		my_socket_client.client_close();
		return 0;
	}
	
	while (!b_end)
	{
		strncpy(buffer, "你好", sizeof(buffer));
		my_socket_client.send_data(buffer, strlen(buffer));
		sleep(1);
		my_socket_client.recv_data(buffer, sizeof(buffer), recv_len);
	}
	

	my_socket_client.client_close();
	return 0;
}

int main()
{
	pthread_t serverid, clientid;
	void *ret;
	int i, retv;
	int t = 123;
	pthread_create(&serverid, NULL, serverthread, (void *)t);

	pthread_create(&clientid, NULL, clientthread, (void *)t);
	while (1) {
		cout<<"main route\n";
		char a[20];
		cin >> a;
		if (strcmp(a, "q") == 0) {
			b_end = true;	
			break;
		}
	}
	void* thread_res;
	pthread_join(serverid, &thread_res);
	return 0;
}

一些问题

  1. 多线程编译
    码完后就可以编译了,但是主函数用了多线程,编译会报错,
    在这里插入图片描述
    我们要在项目属性页->Linker->Command Line加上-lpthread,然后点应用;就可以成功编译了。
    在这里插入图片描述
  2. 中文乱码
    因为我们VS默认的是GBK2312编码,Linux默认的是utf-8,导致了中文乱码,
    在这里插入图片描述
    菜单选择文件->高级保存选项,编码改为UTF-8,行尾改为UNIX(LF)
    在这里插入图片描述
    在这里插入图片描述
    也可以修改…\vs2015\VC\vcprojectitems中的hfile.hnewc++file.cpp文件如下:
    在这里插入图片描述
    在这里插入图片描述
    高级保存选项这两个文件编码为UTF-8,这样以后生成的文件都是utf8格式了。
  3. Linux内sleep()参数的单位是
写的不好,多多见谅。
发布了14 篇原创文章 · 获赞 6 · 访问量 3125

猜你喜欢

转载自blog.csdn.net/qq_39554698/article/details/97134934