之前在腾讯上使用了一个免费的公网服务器,只有7天,linux系统。

其实有这样的想法,是因为有个研二的师弟问我怎么样才能让连个局域网的电脑通信。

我跟他说了两种方法,一种是找个公网服务器来转发数据,另一种就是UDP打洞。

第二种太难了,所以就用第一种。突然有点想自己实现一下的冲动,于是就搞了一个免费的。

目的是:编写一个服务端,接收一个或者多个客户端。如果一个客户端发送数据,则立刻转发给其他的所有连接上的客户端(除了自己)。

TCP的连接程序自然很简单。定义协议后,服务端就开始监听。但是accep函数会在这一步挂起,如果接受了一个客户端就继续向下走了。

所以需要用多线程来实现。我的思想是,accept一个客户端就继续等待一个客户端的连接。

先上代码:

server.c

#include <stdio.h>

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include "server.h" int server_sockfd;
struct sockaddr_in server_address; int vector_user[MaxUser];
struct sockaddr_in vector_useraddr[MaxUser]; int times=; pthread_t transferid; typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler); void sig_int(int sign)
{
while(){
printf("SIGINT\n");
}
}
void sig_pipe(int sign)
{
while(){
printf("SIGPIPE\n");
}
} int SockInit(void)
{
int state=-;
server_sockfd=socket(AF_INET,SOCK_STREAM,);
if(server_sockfd<)
{
printf("Socket error\n");
return -;
}
bzero(&server_address,sizeof(server_address));
server_address.sin_family=AF_INET;
server_address.sin_addr.s_addr=htonl(INADDR_ANY);//inet_addr("115.159.196.190");
server_address.sin_port=htons(PORT); state=bind(server_sockfd,(struct sockaddr*)&server_address,sizeof(server_address));
if(state<)
{
printf("Bind error\n");
return -;
}
state=listen(server_sockfd,);
if(state<)
{
printf("Listen error\n");
return -;
}
printf("Server is waiting and listening on port:%d\n",PORT);
return ;
} int RevData(int cs,char* buf,int maxlen)
{
int len=;
printf("Ready to get data\n");
read(cs,&len,);
printf("Get %d data\n",len);
read(cs,buf,len);
return len;
} void SendData(int cs,char* buf,int len)
{
int sendlen=len;
send(cs,&sendlen,,);
send(cs,buf,len,);
} void* TranferData(void* arg)
{
char buf[]={'\0'};
while()
{
int len=RevData(*(int*)arg,buf,);
int i=;
printf("SocketID is %d: ",*(int*)arg);
for(i=;i<len;i++)
{
printf("%c",buf[i]);
}
printf("\n");
for(i=;i<times;i++)
{
if((*(int*)arg)!=vector_user[i])
{
SendData(vector_user[i],buf,len);
}
}
}
return ((void *));
} void* UserAdd(void* arg)
{
struct sockaddr_in client_address;
socklen_t client_len=sizeof(client_address); while()
{
int client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&client_len);
if(client_sockfd<)
{
printf("Accpet error\n");
}
else
{
printf("%s:%d is connecting\n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
vector_user[times]=client_sockfd;
vector_useraddr[times]=client_address; int err= pthread_create(&(transferid), NULL, TranferData, (void*)(&vector_user[times]));
if ( != err )
{
printf("Can't create thread\n");
} ++times;
if(times>=MaxUser)
{
printf("Up to MaxUser\n");
break;
}
}
}
return ((void *));
}

server.h

#ifndef SERVER_H_INCLUDED

#define SERVER_H_INCLUDED

#define PORT (8888)
#define TencentIP ("115.159.196.190")
#define MaxUser (1024) int SockInit(void);
void* UserAdd(void* arg);
int RevData(int cs,char* buf,int maxlen);
void SendData(int cs,char* buf,int len); #endif // SERVER_H_INCLUDED

main.c

#include <stdio.h>

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include "server.h" extern int times;
extern int vector_user[MaxUser]; int main() {
char buf[];
int state=;
state=SockInit();
if(state<)
{
printf("Socket init error\n");
return -;
} pthread_t threadid;
int err;
err = pthread_create(&(threadid), NULL, UserAdd, NULL);
if ( != err )
{
printf("Can't create thread\n");
return -;
} while()
{ } printf("Hello world!\n"); return ; }

server.c中

SockInit函数就是用来初始化套接字协议的,没什么可说的,无非就是配置一些参数,然后接听端口。

RevData函数,第一个参数传递是套接字的标识符,因为会有多个标识符。其中,先读一个字符,是因为我定义的协议是先发一个长度,然后再发对应长度的数据。(仅是为了方便)

SendData函数也是一样的道理。

TransferData函数是一个线程函数,传入的参数是套接字的标识符。作用就是把收到的数据转发给除它自己以外所有连接的客户端。

UserAdd函数是一个主要的线程函数。它只开启一次。从while(1)开始就等待accept,如果有连接上,就继续向下走,并且开启TransferData函数线程,之后又进入进的accept,等待新的用户来。每当有用户连接上,都会进入这个状态。

其中有有个全局数组,vector_user,vector_useraddr。是用来储存加入进来的用户标识符和地址。

这也是这个程序没有做好的地方:因为如果有客户端掉线,没有那部分代码来去除数组信息,程序就会错误。这是因为,我懒得写一个vector或者其他的数据结构来管理,如果是c++我就用stl了。

还有一个问题:没有管理好如果有用户突然掉线的情况。可以使用signal来管理,但是时间紧迫,没有完全做好。

总之:对于简单粗暴的数据转发是可以的。但是程序没有优化,很容易错误。只是想体验下linux C的开发。希望以后能有机会能涉及到这方面的项目。学会linux下快速调试。

linux下服务端实现公网数据转发的更多相关文章

  1. git 在linux下服务端搭建

    本文以centos为例,其他linux请自行参照对应方式. 1. 服务端安装git yum install git 2. 服务端添加无shell登录权限的用户,将username替换为要添加的用户 u ...

  2. Linux多线程服务端编程一些总结

    能接触这本书是因为上一个项目是用c++开发基于Linux的消息服务器,公司没有使用第三方的网络库,卷起袖子就开撸了.个人因为从业经验较短,主 要负责的是业务方面的编码.本着兴趣自己找了这本书.拿到书就 ...

  3. 《Linux多线程服务端编程:使用muduo C++网络库》上市半年重印两次,总印数达到了9000册

    <Linux多线程服务端编程:使用muduo C++网络库>这本书自今年一月上市以来,半年之内已经重印两次(加上首印,一共是三次印刷),总印数达到了9000册,这在技术书里已经算是相当不错 ...

  4. 《Linux多线程服务端编程》笔记——线程同步精要

    并发编程基本模型 message passing和shared memory. 线程同步的四项原则 尽量最低限度地共享对象,减少需要同步的场合.如果确实需要,优先考虑共享 immutable 对象. ...

  5. 《Linux多线程服务端编程》笔记——多线程服务器的适用场合

    如果要在一台多核机器上提供一种服务或执行一个任务,可用的模式有 运行一个单线程的进程 运行一个多线程的进程 运行多个单线程的进程 运行多个多线程的进程 这些模式之间的比较已经是老生常谈,简单地总结 模 ...

  6. linux下,MySQL默认的数据文档存储目录为/var/lib/mysql。

    0.说明 Linux下更改yum默认安装的mysql路径datadir. linux下,MySQL默认的数据文档存储目录为/var/lib/mysql. 假如要把MySQL目录移到/home/data ...

  7. 一次http请求,谁会先断开TCP连接?什么情况下客户端先断,什么情况下服务端先断?

    我们有2台内部http服务(nginx): 201:这台服务器部署的服务是account.api.91160.com,这个服务是供前端页面调用: 202:这台服务器部署的服务是hdbs.api.911 ...

  8. js插件---WebUploader 如何接收服务端返回的数据

    js插件---WebUploader 如何接收服务端返回的数据 一.总结 一句话总结: uploadSuccess有两个参数,一个是file(上传的文件信息),一个是response(服务器返回的信息 ...

  9. IE8下服务端获取客户端文件的路径为C:/fakePath问题的解决方案

    上一篇文章上提到,IE8下服务端获取客户端文件的路径时,会变成C:/fakePath问题,于是乎通过文件路径去获得文件大小就失败了. 上网搜了一下,主要原因是IE8因为安全考虑,在上传文件时屏蔽了真实 ...

随机推荐

  1. Unity3D 动画回调方法

    最近发现很多coder.在用Unity开发游戏的时候都需要一个需求就是..动画播到某一帧就要干什么事情.而且希望能得到回调. 在unity里面的window菜单有个.Animation工具.打开它.然 ...

  2. CentOS开机无法进入系统,如何查错

    开机时按e/F5按钮,进入选择系统界面 会出现 CentOS(2.6.32-...类似的选择列表,选择默认的系统然后按e: 这时会出现 root kernel ... initd... 三个选项,选择 ...

  3. linux svn hooks代码自动更新至项目

    由于开发移动端web,ui需要及时看到样式变化,所以通过svn hooks(钩子)来提交文件,然后再把文件同步到测试服务器项目目录,步骤如下: 1.进入 /home/svn/cmall/hooks ( ...

  4. ORA-04021:timeout occurred while waiting to lock object

    编译某存储过程 ORA-04021 timeout occurred while waiting to lock object stringstringstringstringstring Cause ...

  5. 安装了多个Oracle11g的客户端,哪个客户端的tnsnames.ora会起作用?

    如果我们由于需要安装了多个Oracle的client,哪个客户端的tnsnames.ora会起作用呢? 答案是: 在安装好clinent端后,安装程序会把client的bin目录放到path里面,pa ...

  6. java.map使用

    Map以按键/数值对的形式存储数据,和数组非常相似,在数组中存在的索引,它们本身也是对象.       Map的接口       Map---实现Map       Map.Entry--Map的内部 ...

  7. OpenSSL漏洞补救办法详解(转)

    CVE-2014-0160漏洞背景 2014年4月7日OpenSSL发布了安全公告,在OpenSSL1.0.1版本中存在严重漏洞(CVE-2014-0160).OpenSSL Heartbleed模块 ...

  8. java程序员需要掌握些什么知识

    java程序员需要掌握些什么知识 合格的程序员应具有实际开发能力的Java和J2EE.如今的IT企业需求量大,但人才紧缺的.企业需要大量掌握Java/JEE/Oracle/WebLogic/Websp ...

  9. go语言环境搭建+sublime text3(windows环境下)

    感觉有点坑,整了一下午~搞定 go语言环境搭建+sublime text3(windows环境下) 1.安装sublime text3 2.安装go语言程序包 3.测试go语言是否安装成功     键 ...

  10. 28. Red Hat Linux安装Vmware Tools

    在VMware虚拟机中安装好了VMware Tools,才能实现主机与虚拟机之间的文件共享,同时可支持自由拖拽的功能,鼠标也可在虚拟机与主机之前自由移动(而不再用按ctrl+alT释放),而且还可以令 ...