安装好环境之后,我们就可以写服务器了,今天先写一个简单的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()参数的单位是秒。