2009年2月3日 星期二

socket - udp

以下有一個簡單的透過UDP來傳送訊息的程式,udp與tcp的差別在於tcp不能由server連續傳訊息給client而必需client-server-client間接傳。
而udp就可以由server連續傳訊息給client
udpserver.c

//==============================include file==========================
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
//====================================================================

//==============================define variable=======================
#define MAXLINE 80
#define SERV_PORT 8888
//====================================================================

//==============================define function=======================
void do_echo(int sockfd,struct sockaddr *pcliaddr,socklen_t clilen);
//====================================================================

//=============================main function==========================
int main(int argc,char *argv[])
{
  int sockfd;
  struct sockaddr_in servaddr,cliaddr;

  // create a socket, using the UDP
  sockfd = socket(AF_INET,SOCK_DGRAM,0);

  // init servaddr
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family=AF_INET;
  servaddr.sin_addr.s_addr=INADDR_ANY;
  servaddr.sin_port=htons(SERV_PORT);

  // bind address and port to socket
  if (bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)
  {
    perror("bind error,the ip address is wrong");
    exit(1);
  }

  do_echo(sockfd,(struct sockaddr*)&cliaddr,sizeof(cliaddr));

  return 0;
}
//====================================================================

//==============================function implement====================
void do_echo(int sockfd,struct sockaddr *pcliaddr,socklen_t clilen)
{
  int n;
  socklen_t len;
  char mesg[MAXLINE];

  for (;;)
  {
    len=clilen;
    // wait for receive data
    n = recvfrom(sockfd,mesg,MAXLINE,0,pcliaddr,&len);
    // sent data back to client
    sendto(sockfd,mesg,n,0,pcliaddr,len);
  }
}
//====================================================================


udpclient.c

//=============================include file================================
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
//=========================================================================

//=============================define variable=============================
#define MAXLINE 80
#define SERV_PORT 8888
//=========================================================================

//=============================define function=============================
void do_cli(FILE *fp,int sockfd,struct sockaddr *pservaddr,socklen_t servlen);
//=========================================================================

//=============================main function===============================
int main(int argc,char *argv[])
{
  int sockfd;
  struct sockaddr_in servaddr;
  char error_message[20];

  // check args
  if (argc!=2)
  {
    printf("usage:udpclient <IP address>\n");
    exit(1);
  }

  // init servaddr
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family=AF_INET;
  servaddr.sin_port=htons(SERV_PORT);
  if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0)
  {
    sprintf(error_message,"[%s] is not a valid IP address\n",argv[1]);
    fputs(error_message,stderr);
    exit(1);
  }

  // build the socket, using the UDP
  sockfd=socket(AF_INET,SOCK_DGRAM,0);

  do_cli(stdin,sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));

  return 0;
}
//=========================================================================


//=============================function implement==========================
void do_cli(FILE *fp,int sockfd,struct sockaddr *pservaddr,socklen_t servlen)
{
  int n;
  char sendline[MAXLINE],recvline[MAXLINE+1];

  // connect to server
  if (connect(sockfd,pservaddr,servlen)==-1)
  {
    perror("connect error");
    exit(1);
  }

  while (fgets(sendline,MAXLINE,fp)!=NULL)
  {
    // read a line and send to server
    write(sockfd,sendline,strlen(sendline));
    // receive data from server
    n=read(sockfd,recvline,MAXLINE);
    if (n==-1)
    {
      perror("read error");
      exit(1);
    }
    // terminate string
    // 依字串的最後面加上一個結束符號
    recvline[n]=0;
    fputs(recvline,stdout);
  }
}
//=========================================================================


有用到的新的東西:
void * memset ( void * ptr, int value, size_t num );
把ptr指標指到的地方,用value的值填滿num個
參考

host to network short
uint16_t htons(uint16_t hostshort);
htons函數用來轉換u_short了來自主機的TCP / IP網絡字節順序(即big-endian )的.
參數hostshort [ ] 16位元數的主機字節順序.
返回值的htons函數返回值的TCP / IP網路字元順序.
須知htons函數有一個16位號碼主機字元順序並返回一個16位數字網路字元
參考
參考2
參考3
htons-htons的功能

FILE * stdin
等待使用者輸入字串,並回傳FILE的指標,與scanf比起來,scanf會以「空白」當作分隔
但是,透過char * fgets ( char * str, int num, stdin );
會把使用者輸入的字串存放到str的指標中,且長度必需小於num-1,如此一來不會以「空白」當作中斷
當我們輸入"good"時,實際上存到str的會是"good\n",也就是在最後面加上一個結束符號。
這裡要注意的是它自動加入的是null
A null character is automatically appended in str after the characters read to signal the end of the C string.
但是非常奇怪的就是用strlen(str)會比原來的長度還要加1,所以,我猜測最後加入的不是'\0'而是'\n',所以,長度才會比原本的多一
這裡就要提到,若使用char *gets( char *str )
不會在最後加入一個結束符號。

FILE *stdout
可以把字串傳送到螢幕上
透過int fputs ( const char * str, stdout);
可以把str指標指到的字串送到螢幕上

FILE *stderr
可以把字串送到錯誤串流
透過int fputs(const char *str,stderr);
可以把str指標指到的字串送到螢幕上

void perror ( const char * str );
perror()用來將上一個函數發生錯誤的原因輸出到標準錯誤(stderr)。參數s所指的字串會先印出,後面再加上錯誤原因字串。此錯誤原因依照全局變量errno的值來決定要輸出的字符串。
若上一個函數沒有執行失敗的話,那就會出現
"訊息":SUCCESS
參考
參考2
參考3
參考4
參考5

上面的一些函數後面都不能加變數,因此可能必需與
int sprintf ( char * str, const char * format, ... );
一起使用。先把所有的變數一起轉成字串,再把字串傳給上面幾個函數使用
參考

int fprintf ( FILE * stream, const char * format, ... );
把字串輸入到檔案中
fprintf(stderr,"there are some errors:%s",string);
回傳值是寫入多少字元

參考資料:
recvfrom
練習 Socket UDP 通訊實驗
socket編程原理
STDIN
編寫Linux下的UDP Client/Server程序

沒有留言: