之前在腾讯上使用了一个免费的公网服务器,只有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. 25+ Useful Selenium Web driver Code Snippets For GUI Testing Automation

    本文总结了使用Selenium Web driver 做页面自动化测试的一些 tips, tricks, snippets. 1. Chrome Driver 如何安装 extensions 两种方式 ...

  2. js 取得 Unix时间戳(Unix timestamp)

    js 取得 Unix时间戳 Unix时间戳(Unix timestamp),或称Unix时间(Unix time).POSIX时间(POSIX time),是一种时间表示方式,定义为从格林威治时间19 ...

  3. JavaScript数据类型转换

    原文转自:http://javascript.ruanyifeng.com/grammar/conversion.html#rd JavaScript是一种动态类型语言,变量是没有类型的,可以随时赋予 ...

  4. 如何彻底卸载Oracle

    如何彻底卸载Oracle 因为Oracle在Windows下的卸载颇有一些麻烦,如果不能完全卸载有可能影响将来的再次安装!常规卸载方法是运行Oracle的自带的卸载程序,可遗憾的是我在卸载时总不能完全 ...

  5. 对​O​p​e​n​C​V​直​方​图​的​数​据​结​构​C​v​H​i​s​t​o​g​r​a​m​的​理​解

    前几天被OpenCV的直方图的数据结构CvHistogram弄得很纠结.上网一搜,也没什么相关的资料.现在有点头绪了,就写点东西,让后面的人好走一些吧. 先来看看CvHistogram的定义: typ ...

  6. django--静态文件(九)

    1.要加载静态文件需要配置: setting.py BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STA ...

  7. 我刚知道的WAP app中meta的属性

    之前我一直做的都是WEB前端开发,来北京以后面试了一个移动前端开发,WAP前端开发. 其实在原来公司的时候也做过这方面的开发,可面试的时候面试官问我,要想强制让文档与设备的宽度保持1:1,mate标签 ...

  8. MFC自绘控件不错的网站收集,不定时更新。

    找资料的时候,遇到好的网站收集起来,当时看看就忘记网址,下次再找又找不到,写下来才记得牢.欢迎大家留言,共同收集. 国外的: 1.codeproject https://www.codeproject ...

  9. 自己用C语言写dsPIC / PIC24 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). HyperBootlo ...

  10. .NET (二)委托第二讲:内置委托Func

    在上一章节中,我们自己声明了一个委托: public delegate bool Cal(int num); 接受int参数,返回bool类型,目的是过滤集合中的 奇数 或者 偶数. .NET 为我们 ...