今天一天又费了一天的时间在linux上写socket程序与windows之前写的kinect程序通讯。这真是艰难而又曲折的尝试啊。。。。。
1.windows作为服务器,windows下mfc程序见上一篇博客http://blog.youkuaiyun.com/u013948010/article/details/78340858
2.linux作为客户端
先在linux下写一个C/S
一开始不能从很难的qt界面写起来,先在linux下写一个服务器和客户端程序互发消息试试,网上的代码多多少少都有些问题,于是开始艰难地改写。其实代码和windows是非常相似的,这里写的是服务器负责收,客户端负责发,改成双向的也是可以的。
服务器端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <iostream>
#define SERVER_PORT 5150
#define BACKLOG 20
#define MAX_MSG_SIZE 1024
int main() {
int sock_fd,client_fd; /*sock_fd:监听socket;client_fd:数据传输socket */
struct sockaddr_in ser_addr; /* 本机地址信息 */
struct sockaddr_in cli_addr; /* 客户端地址信息 */
char msg[MAX_MSG_SIZE];/* 缓冲区*/
int ser_sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建连接的SOCKET */
int res;
if(ser_sockfd<0)
{/*创建失败 */
printf("socker Error\n");
exit(1);
}
/* 初始化服务器地址*/
socklen_t addrlen=sizeof(struct sockaddr_in);
bzero(&ser_addr,addrlen);
ser_addr.sin_family=AF_INET;
ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);
ser_addr.sin_port=htons(SERVER_PORT);
if(bind(ser_sockfd,(struct sockaddr*)&ser_addr,sizeof(struct sockaddr_in))<0){ /*绑定失败 */
printf("Bind Error\n");
exit(1);
}
/*侦听客户端请求*/
if(listen(ser_sockfd,BACKLOG)<0){
printf("Listen Error\n");
close(ser_sockfd);
exit(1);
}
int cli_sockfd=accept(ser_sockfd,(struct sockaddr*)&cli_addr,&addrlen);
if(cli_sockfd<=0){
printf("Accept Error:\n");
return 0;
}
printf("received a connection from %s\n", inet_ntoa(cli_addr.sin_addr));
/*开始服务*/
while(1){/* 等待接收客户连接请求*/
if((res=recv(cli_sockfd,msg,MAX_MSG_SIZE,0))==-1) /* 接受数据*/
{
printf("失去连接\n");
return 0;
}
printf("%s\n",msg);/*在屏幕上打印出来 */
//strcpy(msg,"hi,I am server!");
//send(cli_sockfd,msg,sizeof(msg),0); /*发送的数据*/
}
close(cli_sockfd);
close(ser_sockfd);
return 0;
}
客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#define SERVER_PORT 5150
#define MAX_MSG_SIZE 1024
int main(){
int cli_sockfd;/*客户端SOCKET */
int res;
struct sockaddr_in ser_addr;/* 服务器的地址*/
char msg[MAX_MSG_SIZE];/* 缓冲区*/
cli_sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建连接的SOCKET */
if(cli_sockfd<0){/*创建失败 */
printf("socker Error\n");
exit(1);
}
/* 初始化服务器地址*/
socklen_t addrlen=sizeof(struct sockaddr_in);
bzero(&ser_addr,addrlen);
ser_addr.sin_family=AF_INET;
ser_addr.sin_addr.s_addr = inet_addr("125.216.243.115"); ///服务器ip
ser_addr.sin_port=htons(SERVER_PORT);
if(connect(cli_sockfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr))!=0)/*请求连接*/
{
/*连接失败 */
printf("Connect Error\n");
close(cli_sockfd);
exit(1);
}
strcpy(msg,"hi,I am client!\n");
send(cli_sockfd,msg,sizeof(msg),0);/*发送数据*/
memset(msg,0,MAX_MSG_SIZE);
while(1)
{
if((res=recv(cli_sockfd,msg,MAX_MSG_SIZE,0))==-1) /* 接受数据*/
{
printf("失去连接\n");
return 0;
}
printf("%s\n",msg);/*在屏幕上打印出来 */
}
close(cli_sockfd);
return 0;
}
安装QT
好吧,为了让这个程序看起来比较人性化,必须写个小界面呀,windows下用mfc,linux下就必须用到qt的呀,装QT还得费一番周折。
推荐最傻瓜的方式:
去以上连接直接点下载,注册一下就可以下载到一个 qt-unified-linux-x64-3.0.1-online.run这样的文件,然后在终端
chmod +x qt-unified-linux-x64-3.0.1-online.run
./qt-unified-linux-x64-3.0.1-online.run
就可以自动开启傻瓜式安装,比命令行里面折腾到死去活来或者编译到死去活来要爽!多!了!好不好!
写QT
安装好QT后就可以新建工程然后愉快地拖控件右键添加槽然后把客户端的代码往里面丢了。但是!说起来非常简单,但其中还颇费周折。
其实QT是有自己的socket函数的,但是为了和mfc下的socket通信,必须用很难用的c++ socket有没有!所以这里必须注意的一点是
在循环接收的地方需要建立一个线程去监听来自服务器的消息
读取IP并连接
创建一个按钮用于设置和读取输入框的IP,点击连接创建socket连接,并开启循环监听服务器,这里用了一个ThreadA,下文会写到。
void MainWindow::on_connectRemote_clicked()
{
QString valueStr=ui->ipEdit->text();
ui->connectRemote->setEnabled(false);
struct sockaddr_in ser_addr;/* 服务器的地址*/
cli_sockfd=socket(AF_INET,SOCK_STREAM,0);//创建连接的SOCKET
if(cli_sockfd<0){
//创建失败
ui->textOutput->append("创建socket失败");
exit(0);
}
ui->textOutput->append("创建socket成功");
// 初始化服务器地址
socklen_t addrlen=sizeof(struct sockaddr_in);
bzero(&ser_addr,addrlen);
ser_addr.sin_family=AF_INET;
ser_addr.sin_addr.s_addr = inet_addr("125.216.243.10"); ///服务器ip
ser_addr.sin_port=htons(SERVER_PORT);
if(::connect(cli_sockfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr))!=0)//请求连接
{
//连接失败
ui->textOutput->append("连接失败");
::close(cli_sockfd);
ui->connectRemote->setEnabled(true);
}else{
ui->textOutput->append("已连接到服务器"+valueStr);
threadA.start();
}
}
运行一下,windows服务器那边显示已连接,就是成功了
发送消息按钮
void MainWindow::on_send_clicked()
{
QString valueStr=ui->inputEdit->toPlainText();
char msg[MAX_MSG_SIZE];/* 缓冲区*/
QByteArray ba = valueStr.toLatin1();
char *mm = ba.data();
strcpy(msg,mm);
if(send(cli_sockfd,msg,sizeof(msg),0)==-1){
/*发送数据*/
ui->textOutput->append("发送失败");
}else{
memset(msg,0,MAX_MSG_SIZE);
ui->textOutput->append("client:"+valueStr);
ui->inputEdit->setPlainText("");
}
}
有点麻烦的线程
线程的话,必须新建一个线程类,Thread,其中还包括一个signals函数sendData(QString)用于和主窗口发消息,不然监听到的服务器消息是不能显示到主窗口上的。
头文件是这样的:
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include "mainwindow.h"
class Thread:public QThread
{
Q_OBJECT
public:
Thread();
void setMessage(QString message);
void stop();
protected:
void run();
private:
QString messageStr;
volatile bool stopped;
void printMessage();
signals:
void sendData(QString); //用来传递数据的信号
};
#endif // THREAD_H
实现文件.cpp
#include "thread.h"
#include <QDebug>
extern int cli_sockfd;
Thread::Thread()
{
stopped = false;
}
void Thread::run()
{
int res;
char msg[MAX_MSG_SIZE];/* 缓冲区*/
while (1) {
if((res=recv(cli_sockfd,msg,MAX_MSG_SIZE,0))==-1) //接受数据
{
emit sendData("失去连接\n");
return;
}else{
emit sendData(QString(msg));
memset(msg,0,MAX_MSG_SIZE);
}
}
}
void Thread::stop()
{
stopped = true;
}
void Thread::setMessage(QString message)
{
messageStr = message;
}
主窗体函数必须加一个接收消息的函数并绑定他们俩,参考http://blog.youkuaiyun.com/zbw1185/article/details/48519371
void MainWindow::receiveData(QString data){
ui->textOutput->append("server:"+data);
}
绑定:
connect(&threadA, SIGNAL(sendData(QString)), this, SLOT(receiveData(QString))); //关联发送和接受函数
大概可以比较愉快地运行了
这是linux客户端的截图
windows mfc服务器截图
最后
还有个乌七八糟的bug就是linux这边把socket close之后,windows那边并没有立刻关闭的呀,所以只能在close的时候设一个标志消息,windows那边判断一下再关闭。关闭时把监听线程也关掉。
void MainWindow::on_click_clicked()
{
char *mm="leave";
send(cli_sockfd,mm,sizeof(mm),0);
::close(cli_sockfd);
if(threadA.isRunning()){
threadA.terminate();
threadA.wait();
}
exit(0);
}