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

it2022-05-08  8

安装好环境之后,我们就可以写服务器了,今天先写一个简单的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; }

一些问题

多线程编译 码完后就可以编译了,但是主函数用了多线程,编译会报错, 我们要在项目属性页->Linker->Command Line加上-lpthread,然后点应用;就可以成功编译了。 中文乱码 因为我们VS默认的是GBK2312编码,Linux默认的是utf-8,导致了中文乱码, 菜单选择文件->高级保存选项,编码改为UTF-8,行尾改为UNIX(LF)。 也可以修改…\vs2015\VC\vcprojectitems中的hfile.h和newc++file.cpp文件如下: 并高级保存选项这两个文件编码为UTF-8,这样以后生成的文件都是utf8格式了。Linux内sleep()参数的单位是秒。
写的不好,多多见谅。

最新回复(0)