[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 編程技術 > 其它 > 正文
Linux程式設計- 4.socket
http://www.openchess.org/noitatsko/programming/ (2001-05-24 17:24:26)
UNIX Socket Programming基本上是一本書名。Socket programming其實需要相當程度的基礎,我不想在這包山包海地,如果您需要徹底研究,可以買這本書來看。在此我想提供一些簡單的Server/Client兩端的簡單寫法,讓你有個起點,做為進一步研究的基礎。很多涉及較復雜的內容的,我在這便不詳細說明,您可以照本宣科,照抄著用,稍微熟悉時,再細細研究。 

--------------------------------------------------------------------------------

Client
int sock_connect(char *domain,int port) 

  int white_sock; 
  struct hostent * site; 
  struct sockaddr_in me; 
  site = gethostbyname(domain); 
  if (site==NULL) return -2; 

  white_sock = socket(AF_INET,SOCK_STREAM,0); 
  if (white_sock<0) return -1; 

  memset(&me,0,sizeof(struct sockaddr_in)); 
  memcpy(&me.sin_addr,site->h_addr_list[0],site->h_length); 
  me.sin_family = AF_INET; 
  me.sin_port = htons(port); 

  return (connect(white_sock,(struct sockaddr *)&me,sizeof(struct sockaddr))<0) ? -1 : white_sock; 


要由Client向伺服器端要求連線的步驟,首先您必須要找出對方的位址,可利用: 

gethostbyname() 

接下來要建立起一個socket,然後用這個socket來建立連線。 

接下來我們利用這個簡單的socket程式來寫一個讀取WWW網頁的簡單瀏覽器(看html source)。 
#include  
#include  
#include  
#include  
#include  
#include  
#include  

int htconnect(char *domain,int port) 

  int white_sock; 
  struct hostent * site; 
  struct sockaddr_in me; 

  site = gethostbyname(domain); 
  if (site==NULL) return -2; 
  

  white_sock = socket(AF_INET,SOCK_STREAM,0); 
  if (white_sock<0) return -1; 

  memset(&me,0,sizeof(struct sockaddr_in)); 
  memcpy(&me.sin_addr,site->h_addr_list[0],site->h_length); 
  me.sin_family = AF_INET; 
  me.sin_port = htons(port); 

  return (connect(white_sock,(struct sockaddr *)&me,sizeof(struct sockaddr))<0) ? -1 : white_sock; 


int htsend(int sock,char *fmt,...) 

  char BUF[1024]; 
  va_list argptr; 
  va_start(argptr,fmt); 
  vsprintf(BUF,fmt,argptr); 
  va_end(argptr); 
  return send(sock,BUF,strlen(BUF),0); 


void main(int argc,char **argv) 

  int black_sock; 
  char bugs_bunny[3]; 

  if (argc<2) return; 

  black_sock = htconnect(argv[1],80); 
  if (black_sock<0) return; 
  htsend(black_sock,"GET / HTTP/1.0%c",10); 
  htsend(black_sock,"Host: %s%c",argv[1],10); 
  htsend(black_sock,"%c",10); 
  while (read(black_sock,bugs_bunny,1)>0) { printf("%c",bugs_bunny[0]); } 

  close(black_sock); 


編譯:
gcc -o ex1 client.c 
執行
./ex1 www.linux.org.tw 


--------------------------------------------------------------------------------

Server
Listen to a port
要建立起一個網路伺服器,第一步就是要"傾遠方",也就是要Listen。 
以下是一般建立服務的方法: 
int DaemonSocket; 
struct sockaddr_in DaemonAddr; 
int BindSocket(void) 


  DaemonSocket = socket(AF_INET,SOCK_STREAM,0); 
  if (DaemonSocket==-1) return 0; 
  DaemonAddr.sin_family = AF_INET; 
  DaemonAddr.sin_port   = htons(DAEMON_PORT); 
  if (bind(DaemonSocket,&DaemonAddr,sizeof(DaemonAddr))<0) { 
    printf("Can not bind!\n"); 
    return 0; 
  } 
  if (listen(DaemonSocket,1024)!=0) { 
    printf("Can not listen!\n"); 
    return 0; 
  } 

  return 1; 


Incoming call
要查看是否有連線進來,可用以下方式: 
int incoming_call(void) 

  fd_set sock; 
  struct timeval tv; 
  int t; 

  FD_ZERO(&sock); 
  FD_SET(DaemonSocket,&sock); 
  tv.tv_sec = 60; tv.tv_usec = 0; 
  t = select(DaemonSocket + 1,&sock,NULL,NULL,&tv); 
  if (t<=0||!FD_ISSET(DaemonSocket,&sock)) return 0; 

  printf("incoming...\n"); 

  return 1; 


Connect Client
當我們確認有人進來要求服務時,會需要accept connection,可用以下方式 
int ConnectClient(void) 

  int socksize=sizeof(HostAddr); 
  unsigned char * addr; 

  ClientSocket = accept(DaemonSocket,(struct sockaddr*)&HostAddr,&socksize); 
  if (ClientSocket<0) return 0; 

  addr = (unsigned char *)&HostAddr.sin_addr.s_addr; 

  printf("incoming address:%d.%d.%d.%d\n",addr[0],addr[1],addr[2],addr[3]); 

  return 1; 


注意到當您accept connection之後,連線已建立起,此時要用的socket是ClientSocket,而非DaemonSocket,ClientSocket才是真正用來連線用的socket。 

這是個我才剛開始動手寫的象棋伺服器。 

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  

#define DAEMON_LOCK "/var/chess/daemon.lock" 
#define DAEMON_LOG  "/var/chess/daemon.log" 
#define DAEMON_PORT 9901 

int DaemonSocket; 
struct sockaddr_in DaemonAddr; 

int ClientSocket=0; 
struct sockaddr_in HostAddr; 

void dlog(char *fmt,...) 

  va_list argptr; 
  FILE *fp; 

  fp = fopen(DAEMON_LOG,"a+t"); 
  va_start(argptr,fmt); 
  vfprintf(fp,fmt,argptr); 
  va_end(argptr); 
  fclose(fp); 


pid_t CheckLock(void) 

  pid_t me; 
  FILE * fp; 

  fp = fopen(DAEMON_LOCK,"rt"); 
  if (fp==NULL) return 0; 
  fscanf(fp,"%d",&me); 
  fclose(fp); 

  return me; 


pid_t WriteLock(void) 

  pid_t me; 
  FILE *fp; 

  me = getpid(); 

  fp = fopen(DAEMON_LOCK,"w"); 
  fprintf(fp,"%d",me); 
  fclose(fp); 

  return me; 


int CleanLock(void) 

  return (unlink(DAEMON_LOCK)==0); 


void report_time(void) 

  time_t now; 
  now = time(NULL); 
  dlog("%s",asctime((const struct tm*)localtime(&now))); 


static void signal_catch(int signo) 

  time_t now; 

  close(DaemonSocket); 
  if (ClientSocket>0) close(ClientSocket); 
  CleanLock(); 
  now = time(NULL); 
  dlog("Catch signal %d, leave at %s\n",signo,asctime((const struct tm*)localti 
  exit(-1); 


void SetupSignal(void) 

  struct sigaction act; 

  act.sa_handler = signal_catch; 
  act.sa_flags   = 0; 
  sigemptyset(&act.sa_mask); 
  sigaction(SIGHUP,&act,NULL); 
  sigaction(SIGINT,&act,NULL); 
  sigaction(SIGQUIT,&act,NULL); 
  sigaction(SIGILL,&act,NULL); 
  sigaction(SIGABRT,&act,NULL); 
  sigaction(SIGIOT,&act,NULL); 
  sigaction(SIGBUS,&act,NULL); 
  sigaction(SIGFPE,&act,NULL); 
  sigaction(SIGTERM,&act,NULL); 


int BindSocket(void) 


  DaemonSocket = socket(AF_INET,SOCK_STREAM,0); 
  if (DaemonSocket==-1) return 0; 
  DaemonAddr.sin_family = AF_INET; 
  DaemonAddr.sin_port   = htons(DAEMON_PORT); 
  if (bind(DaemonSocket,&DaemonAddr,sizeof(DaemonAddr))<0) { 
    printf("Can not bind!\n"); 
    return 0; 
  } 
  if (listen(DaemonSocket,1024)!=0) { 
    printf("Can not listen!\n"); 
    return 0; 
  } 

  return 1; 


int incoming_call(void) 

  fd_set sock; 
  struct timeval tv; 
  int t; 

  FD_ZERO(&sock); 
  FD_SET(DaemonSocket,&sock); 
  tv.tv_sec = 60; tv.tv_usec = 0; 
  t = select(DaemonSocket + 1,&sock,NULL,NULL,&tv); 
  if (t<=0||!FD_ISSET(DaemonSocket,&sock)) return 0; 

  dlog("incoming...\n"); 

  return 1; 


int ConnectClient(void) 

  int socksize=sizeof(HostAddr); 
  unsigned char * addr; 

  ClientSocket = accept(DaemonSocket,(struct sockaddr*)&HostAddr,&socksize); 
  if (ClientSocket<0) return 0; 

  addr = (unsigned char *)&HostAddr.sin_addr.s_addr; 

  dlog("incoming address:%d.%d.%d.%d\n",addr[0],addr[1],addr[2],addr[3]); 

  return 1; 


int daemon_printf(char *fmt,...) 

  char BUF[4096]; 
  va_list argptr; 

  va_start(argptr,fmt); 
  vsprintf(BUF,fmt,argptr); 
  va_end(argptr); 
  return write(ClientSocket,BUF,strlen(BUF)); 


void Log(void) 

  char BUF[4096]; 
  read(DaemonSocket,BUF,16); 
  daemon_printf("%s",BUF[0]); 


int main(int argc,char **argv) 

  pid_t myself; 
  time_t now; 

  /* find myself */ 
  myself = CheckLock(); 
  if (myself!=0) { 
    printf("Existing a copy of chess daemon[pid=%d], leave now.\n",myself); 
    exit(1); 
  } 

  /* fork */ 
  myself = fork(); 
  if (myself>0) { 
    exit(1); 
  } else 
  if (myself<0) { 
    printf("Strange world! I don't like it. Quit because of pid=%d\n",myself); 
    exit(1); 
  } else { 
    SetupSignal(); 
    if (!BindSocket()) { 
      printf("Can not bind socket!\n"); 
      exit(1); 
    } 
    WriteLock(); 
  } 

  printf("Chess Daemon is up, have fun!\n"); 

  now = time(NULL); 

  dlog("----------------------------------------------\n"); 
  dlog( 
    "I am back! %s" 
    "Chess Daemon comes to alive again.\n", 
    asctime((const struct tm*)localtime(&now)) 
  ); 

  do { 
    if (incoming_call()) { 

      if (ConnectClient()) { 

        fd_set sock; 
        struct timeval tv; 
        int t; 
        char BUF[128]; 
        char CC[2]; 
        int n; 

          daemon_printf("Welcome to Chinese Chess Game Center!\n"); 

          FD_ZERO(&sock); 
          FD_SET(ClientSocket,&sock); 
          n = 0; 
          do { 
            tv.tv_sec = 60; tv.tv_usec = 0; 
            t = select(ClientSocket+1,&sock,NULL,NULL,&tv); 
            if (t<=0||!FD_ISSET(ClientSocket,&sock)) ; 
            read(ClientSocket,CC,1); 
            if (CC[0]==13||CC[0]==10||CC[0]==0) { 
              BUF[n] = 0; 
              dlog("%s\n",BUF); 
              if (strncasecmp(BUF,"exit",4)==0) { 
                close(ClientSocket); 
                break; 
              } 
              n = 0; 
            } else { 
              BUF[n]=CC[0]; n++; 
            } 
          } while (1); 
      } 
    } 
  } while (1); 

  return 1; 


檢驗
telnet localhost 9901 
  
在處理Connect Client時,事實上可以運用fork或thread來處理多個連線。 
(http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
Linux程式設計-31.工作群資訊管理(grp) (2001-05-27 22:08:00)
Linux程式設計-30.使用者資訊管理(pwd) (2001-05-27 21:04:00)
Linux程式設計-29.時間處理 (2001-05-27 20:10:01)
Linux程式設計-28.GNU Make (2001-05-27 19:00:00)
Linux程式設計-27.GNU Debugger (2001-05-27 18:08:01)
Linux程式設計-26.PIPE (2001-05-27 17:04:00)
Linux程式設計-25.Message Queues (2001-05-27 16:10:00)
Linux程式設計-24.Semaphores (2001-05-27 15:00:00)
Linux程式設計-23.共享記憶體(Shared Memory) (2001-05-27 14:08:00)
Linux程式設計-20.getopt (2001-05-27 13:04:00)

===更多相關===
 

★  樊強制作 歡迎分享  ★