SOCKET简单实例

客户端

WSAStartup→socket→サービス指定→connect→send→closesocket→WSACleanup

#define TCP_FLG    1
#define UDP_FLG    0
#define PORT_IP_REMOCON (60000)
#define IP_SERVER ("192.168.168.113")
#define MAX_LENGTH 100

static SOCKET g_socket = INVALID_SOCKET;
static SOCKADDR_IN g_sockaddr ;
static char g_command[MAX_LENGTH];
void main(){
  
int ret = 0;
  memset(
&g_command,0,sizeof(g_command));

  CreateSocket(TCP_FLG);
  printf(
"スレーブが起動しました、送信データをご入力ください。use →~closesocket~close SendData --");
  
while(1){
    gets(
&g_command);
    
if (strcmp(&g_command,"closesocket"== 0){
      CloseSocket();
      
break;
    }

    
else
    
{
      SendData();
    }

    printf(
"SendData --");
  }

  exit(
1);
}


int CreateSocket(int protocol){
  
int type ;
  
int sts ;
  
int error_no = 0;
  WSADATA wsData;
  WSAStartup(MAKEWORD(
22),&wsData);

  memset(
&g_sockaddr, 0sizeof(g_sockaddr));
  g_sockaddr.sin_addr.s_addr 
= inet_addr(IP_SERVER);
  
//表示在Internet域中使用
  g_sockaddr.sin_family = AF_INET;
  
//将主机的unsigned long????网?字??序(32位):
  g_sockaddr.sin_port = htons(PORT_IP_REMOCON);

  
if (protocol == TCP_FLG ){
    type 
= SOCK_STREAM; 
  }
else if(protocol == UDP_FLG){
    type 
= SOCK_DGRAM;
  }

  
  g_socket 
= socket(AF_INET,type,0);
  
if (g_socket == SOCKET_ERROR ){
    error_no 
= WSAGetLastError();
    
return -1;
  }

  
  sts 
= connect(g_socket, (struct SOCKADDR_IN *)&g_sockaddr , (int)sizeof(g_sockaddr));
  
if (sts < 0 ){
    error_no 
= WSAGetLastError();
    
return -2;
  }



}


int SendData(void){
  send(g_socket,
&g_command,sizeof(g_command),0);
}


void CloseSocket(void){
  closesocket(g_socket);
  g_socket 
=INVALID_SOCKET;
  WSACleanup();
}

 服务器(单线程)

WSAStartup→socket→bind→listen→accept→recv→closesocket→WSACleanup

 

#define TCP_FLG    1
#define UDP_FLG    0
#define SERVER_PORT  60000
#define SERVER_IP  ("192.168.168.113")
#define STDIN 0 /* file descriptor for standard input */ 
static SOCKET g_socket = INVALID_SOCKET;
static SOCKET g_client_socket ;
static SOCKADDR_IN g_sockaddr ;

void main(){
    
int sts = 0;
    sts 
= CreateSocket(TCP_FLG);
    
if (sts == -1){
      printf(
"起動失敗。");
    }

    ReceiveData();
    CloseSocket();
}


int CreateSocket(int type){
  WSADATA wsaData;
  
int protocol;
  
int sts;
  
int nfds;


  WSAStartup(MAKEWORD(
2,2),&wsaData);
  
  
if(type == TCP_FLG ){
    protocol 
= SOCK_STREAM;
  }
else if (type == UDP_FLG){
    protocol 
= SOCK_DGRAM;
  }


  g_socket 
= socket(AF_INET,protocol,0);
  memset(
&g_sockaddr, 0sizeof(g_sockaddr));
  g_sockaddr.sin_family 
= AF_INET;
  g_sockaddr.sin_port 
= htons(SERVER_PORT);
  g_sockaddr.sin_addr.S_un.S_addr 
= inet_addr(SERVER_IP);
  bind(g_socket,(SOCKADDR_IN 
*)&g_sockaddr,sizeof(g_sockaddr));
  sts 
= listen(g_socket,1);
  printf(
"マスタを起動します............. ");
}


int ReceiveData(void){
  
char g_receive[100];
  
int addrlen = (int)sizeof(g_sockaddr);

  
while(1){
    g_client_socket 
= accept(g_socket,(SOCKADDR_IN*)&g_sockaddr,&addrlen);
    
if ((SOCKET)g_client_socket != INVALID_SOCKET ){
      printf(
"連続が入りました............. ");
      
break;
    }

    Sleep(
2000);
    
break;
  }


  memset(
&g_receive, 0sizeof(g_receive));
  
while(1){
    recv(g_client_socket,
&g_receive, sizeof(g_receive), 0);
    
if (strcmp(g_receive,""!= 0){
      printf(
"%s データは取得しました。............. ",g_receive);
      memset(
&g_receive, 0sizeof(g_receive));
    }

    Sleep(
2000);
  }

}


void CloseSocket(void){
  closesocket(g_socket);
  WSACleanup();
}

 

多线成

轮询"会使CPU处于忙等待方式,从而降低性能,浪费系统资源。而调用 select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件 描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费 CPU开销。Select函数原型为:
int select(int numfds,fd_set *readfds,fd_set *writefds,
fd_set *exceptfds,struct timeval *timeout);
   其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以 从标准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中;numfds的值 是需要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1;当select返回时,readfds将被修改,指示某个文件 描述符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏:
   FD_ZERO(fd_set *set)----清除一个文件描述符集;
   FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中;
   FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除;
   FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。
   Timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为:
   struct timeval {
   int tv_sec; /* seconds */
   int tv_usec; /* microseconds */
};

 

void main(){
  WSADATA wsData;
  fd_set readfds;
  fd_set fds;
  
struct timeval tv;
  
int sts;

  WSAStartup(MAKEWORD(
22),&wsData);
  CreateSocket(TCP_FLG);
  memset(
&g_receive,0,sizeof(g_receive));

  tv.tv_sec 
= 10
  tv.tv_usec 
= 5000000

  FD_ZERO(
&readfds);
  FD_SET(g_client_socket,
&readfds);
  
while(1){
   
// memcpy(&fds, &readfds, sizeof(readfds));
    sts = select(g_client_socket+1&readfds, NULL, NULL, &tv);
    
if (FD_ISSET(g_client_socket,&readfds)){
      
      
//g_client_socket = accept(g_socket,&g_sockaddr,&addlen);
      recv(g_client_socket,&g_receive, sizeof(g_receive), 0);
      FD_SET(g_client_socket,
&readfds);
    }

  }

}
结构层次及相互联系 (1)、工作线程:响应连接的IO投递返回并负责投递读请求,并将IO返回结果投递给处理线程,可设定参数决定工作线程数量; (2)、处理线程:处理线程调用回调函数将信息传递给应用层或协议栈,可设定参数决定工作处理数量; (3)、看守线程:响应Accept事件调用AcceptEx,检测连接和心跳超时 ,将信息投递给工作线程,模块仅有一个看守线程。 1. 技术要求 (1)、线程同步:Lock指令、临界段; (2)、主要Socket API:WSASend、WSARecv、AcceptEx、DisconnectEx; (3)、内存管理:连接池(句柄重用)、内存池; (4)、数据0拷贝:通过内置处理线程,上层应用可以避免自建线程池及复制数据的过程。同时提供GBuf内存分配功能,应用层获得分配地址及填充数据之后亦可直接投递给内核/驱动层; (5)、数据顺序同步:同一个连接同时只有一个处理线程响应其IO事件; (6)、IO请求投递:单投递读、多投递写; (7)、0缓冲读投递:可条件编译实现,以适用大规模连接要求。 (8)、超时机制:可设置空连接(连接不发送数据)超时时间以防止DOS攻击,也可设置心跳超时时间防止网络故障导致的现有连接成为虚连接避免耗尽系统资源。 (9)、接口技术:API、回调函数、客户句柄(客户连接句柄)。 (10)、主、被动发送:不使用HASH、MAP及LIST技术,即可提供安全可靠高效的客户连接句柄,以实现服务器端主被动发送数据功能; (11)、PerHandleData的回收不以IO投递的计数器或链表来做依据但仍能安全回收,同时尽量避免在高频的读写操作时做其他无关的操作以提高读写效率。 (12)、处理线程和工作线程有着良好分工界限,繁重的工作交给处理线程完成,工作线程工作量最大限度的减少,仅响应投递返回及读投递的操作; (13)、支持AWE,模块自动识别AWE是否开启(需手动开启),“否”则使用虚拟内存机制。 2. 功能要求 (1)、多IP多端口监听,每个监听可设置不同的回调函数,以高效的区别处理数据 (2)、可设置每秒最大的连接并发量和空连接(连接不发数据)超时时间以防止DOS攻击造成的服务瘫痪、具有心跳处理(防网络异常造成的虚连接)功能 (3)、不加协议的透明传输,可适用广泛的网络通讯环境 (4)、可现实主、被动发送数据,但不会因兼顾主动发送而额外增加降低效率的工作 (5)、内置处理线程,上层应用可不必自建线程池处理数据,所有IO事件按顺序调用回调函数并可以在回调函数内直接处理数据,不必担心多线程造成的接收数据乱序的问题。 (6)、高效率的数据对应关联机制,在初次连接并根据登录数据设置每个连接对应的宿主(Owner)之后,再接收的数据即可立即获得该连接对应的宿主,而不必再做额外的查询工作,并且模块内部采用的是指针关联方式,对于长连接、主动发送的服务器系统而言是高效率的。 (7)、可兼容IPv6 3. 注意事项 因硬件环境和应用环境不同,不合理的配置会出现效率及性能上的问题,因此以下情况出现时,请务必与作者联系以确保获得更好的参数配置: (1)、连接量超过1000个的。超过的应结合具体硬件配置和网络带宽等因素综合设定运行参数。 (2)、带宽使用率超过20%的。工作线程和处理线程数量的设置也是综合考虑数据吞吐量和数据处理负载的因素来设置的,过多的线程会在调度上浪费时间,同时也应该综合考虑线程优先级别来设置工作线程和处理线程数量,两者的设置也不一定能相等。 (3)、服务器端有主动发送需求的、短连接(含网络故障造成的连接断开)出现频率高的。 压力测试工具介绍: 一、 使用G-TcpClient模块 二、 可以设定间隔时间发起大规模长、短连接 三、 可以发起密集数据包,包括即时和定时发送,1M的光纤带宽最大可以达到100K/S(单向)以上,100M本地网最大可以达到10M/S(单向)以上 四、 数据发送仅由一个独立线程但当,每点击一次Connect就创建一个线程根据当前参数发起连接。 五、 测试前提:服务器接收客户端数据后立即原样返回给客户端
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值