日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux Socket学习(十八)--完

發(fā)布時間:2025/3/21 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux Socket学习(十八)--完 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一個實際的網(wǎng)絡(luò)工程

不論我們的頭腦是否在由上一章的學(xué)習(xí)中清醒過來,現(xiàn)在我們需要休息一下了。在這一章我們并不討論新的內(nèi)容,而是用我們所學(xué)到的這些東西來實現(xiàn)一些有趣的事情。在學(xué)習(xí)了這么多的東西之后來一些有趣的東西是十分重要的。

在這一章,我們將會:

應(yīng)用TCP/IP套接口從網(wǎng)上下載股票行情信息
應(yīng)用UDP廣播和我們的局域網(wǎng)內(nèi)發(fā)布股票行情信息
使用UDP客戶端程序來接收局域網(wǎng)廣播的股票行情信息

問題描述

在提供解決方案之前進行問題描述是一個很好的習(xí)慣。所以,在這里我們要描述一下在這一章我們將會解決的問題。

我們有一個全職或是兼職的小公司。我們的辦公室很小,所以免費得到一些行情信息是十分重要的。另外,我們需要關(guān)注我們的主機也網(wǎng)絡(luò)提供商之間的網(wǎng)絡(luò)擁塞,因為我們還需要為其他的事情準(zhǔn)備網(wǎng)絡(luò)帶寬。同時,我們的公司職員也堅持得到最好的免費的行情信息。

解決行情服務(wù)問題

由問題描述我們可以很清楚的看出我們需要由行情提供者處得到一組行情信息。我們沒有理由使得我們的公司員工使用單獨的TCP/IP連接到同一個行情提供者來獲得同樣的信息。這會耗盡寶貴的網(wǎng)絡(luò)帶寬。從免費的行情提供者的角度來看,這也是不必要的。

一個本地服務(wù)器程序可以持續(xù)的為大家獲取更新的股票市場行情信息。然后,這個信息可以播報到網(wǎng)絡(luò)中的所有參與者。這就是我們在這一章將會提供的解決方案。這個程序也會使得我們回憶復(fù)習(xí)流式套接口與數(shù)據(jù)報套接口。

得到股票市場行情

我們要使用的市場行情信息的數(shù)據(jù)源為finance.yahoo.com。在這一部分,我們將會了解程序如何由finance.yahoo.com獲取行情信息。

為 了確定如何獵取市場行情,我們可以使用網(wǎng)絡(luò)瀏覽器來訪問htt://finance.yahoo.com。此時,網(wǎng)站會提供給我們一個頁面,允許我們輸入 符號并且點擊Get Quotes按鈕。輸入RHAT并且按下按鈕會將我們引入另一個頁面,在這里我們可以得到關(guān)于Red Hat Inc的詳細(xì)信息。在行情信息的底部,我們可以看到一個名為Download Spreadsheet Format的鏈接。這就是金礦所在。

在這里,我們有三個選擇:

將鼠標(biāo)移動該鏈接上,并且注意在狀態(tài)行所顯示的URL信息
從 菜單中選擇View->Page Source。查看整個HTML代碼,找到如下的部分:<a href="/d/ quotes.csv?s=RHAT&f=sl1d1t1c1ohgv&e=.csv">Download Spreadsheet Format</a>

在Netscape中獲取這個信息的最好方法就是在鏈接上點擊右鍵。從彈出菜單中選擇Copy Link Location。這會將鏈接引用拷貝到剪切板中。然后,我們可以將這個信息粘貼到文件中。

從這個信息,我們可以得到我們需要的全部分內(nèi)容。我們可以使用telnet來試驗這些內(nèi)容:

$ telnet finance.yahoo.com 80
Trying 204.71.201.75 . . .
Connected to finance.yahoo.com.
Escape character is '^]'.
GET /d/quotes.csv?s=RHAT&f=sl1d1t1c1ohgv&e=.csv
"RHAT",168.9375,"11/24/1999","4:00PM",0,147.5625,175.5,145.6875,3061700
Connection closed by foreign host.
$

在我們連接之后執(zhí)行GET命令,同時指定一個奇怪的路徑名并按下回車鍵。如果命令執(zhí)行成功,我們就會得到一行擴展數(shù)據(jù)。將RHAT替換為另一個代碼我們就會得到不同的數(shù)據(jù)。

如 果我們沒有這樣好的運氣,那么我們需要檢查一下我們的拼寫。精確在這里是非常重要的。如果可能可以使用剪切粘貼的操作。如果仍不起作用,那么我們需要查看 一下finance.yahoo.com現(xiàn)在是如何工作的。由前面我們所指出的步驟框架我們可以發(fā)現(xiàn)所需要的新的主機名與路徑名。

下面是行情獲取步驟的小結(jié):

1 在80商品連接到finance.yahoo.com
2 使用所顯示的路徑名來執(zhí)行GET請求
3 一行表格格式數(shù)據(jù)會作為響應(yīng)返回套接口
4 關(guān)閉套接口

這就是所有的步驟。當(dāng)我們檢測后面所列出的代碼時我們要記住這些步驟。

后面的章節(jié)內(nèi)容會演示并描述組成服務(wù)器與客戶端程序所需要的各個模塊。然而由于空間的原因,我們在這里并不能列出全部的代碼。

下表列出了我們所需要的源碼文件。其中一些有趣的部分會在這一章進行相關(guān)的描述。

源文件?? ??? ?描述
Makefile?? ?工程make文件
bcast.c?? ??? ?實現(xiàn)執(zhí)行向局域網(wǎng)廣播的函數(shù)
connect.c?? ?實現(xiàn)執(zhí)行連接到遠(yuǎn)程行情服務(wù)器的函數(shù)
csvparse.c?? ?分析返回的行情數(shù)據(jù)
gettick.c?? ?從遠(yuǎn)程網(wǎng)行情服務(wù)器得到行情信息
load.c?? ??? ?由文件tickers.rc裝入股票市場代碼
misc.c?? ??? ?一些小函數(shù)
mkaddr.c?? ?網(wǎng)絡(luò)地址函數(shù)
mkwatch.c?? ?市場監(jiān)測客戶端程序。這個程序會接收本地廣播信息并顯示
msgf.c?? ??? ?實現(xiàn)本地行情服務(wù)器的模塊
quotes.h?? ?所有源模塊所用的通常頭文件
tickers.rc?? ?股票代碼文件

檢測行情服務(wù)器程序

我們將要開始檢測源碼模塊為qserve.c源碼模塊。這個模塊為行情服務(wù)器本身形成主程序。他負(fù)責(zé)獲取股票市場行情并且向局域網(wǎng)進行廣播。下面列出了qserve.c的源代碼:
/*
?* qserve.c:
?*
?* Stock Quote Connentrator Program:
?*/
#include "quotes.h"

static char *command = NULL;

/*
?* Remote Quote Server Address */
static char *cmdopt_a = DFLT_SERVER;

/*
?* Quote Re-Broadcase Address
?*/
static char *cmdopt_b = DFLT_BCASE;

/*
?* Ticker Table:
?*/
static TickReq tickers[MAX_TICKERS];
static int ntick=0;

/*
?* Return server usage information:
?*/
static void usage(void)
{
?? ?printf("Usage: %s [-h] [-a address:port]/n"
?? ??? ??? ?"where:/n"
?? ??? ??? ?"/t-h/t/tRequests usage info./n"
?? ??? ??? ?"/t-a address:port/tSpecify "
?? ??? ??? ?"the server/n"
?? ??? ??? ?"/t/t/taddress and port number./n"
?? ??? ??? ?"/t-b bcastport /tSpecify "
?? ??? ??? ?"the broadcase/n"
?? ??? ??? ?"/t/t/taddress and port number./n",
?? ??? ??? ?command);
}

/*
?* Server main program:
?*/
int main(int argc,char **argv)
{
?? ?int rc=0;?? ??? ?/* Return code */
?? ?int optch;?? ??? ?/* Option char */
?? ?int z;?? ??? ??? ?/* Status code */
?? ?int x;?? ??? ??? ?/* Index */
?? ?int s;?? ??? ??? ?/* Broadcast socket */
?? ?time_t tn=0;?? ?/* Time Next */
?? ?time_t zzz;?? ??? ?/* Sleep Time */
?? ?time_t td;?? ??? ?/* Time & Date */
?? ?struct sockaddr_in bc_addr;?? ??? ?/* bc addr */
?? ?socklen_t bc_len;?? ??? ??? ??? ?/* bc addr len */
?? ?const int True = TRUE;?? ??? ??? ?/* const TRUE */
?? ?static char cmdopts[]="ha:b:";

?? ?/*
?? ? * Process command line options:
?? ? */
?? ?command = Basename(argv[0]);

?? ?while((optch = getopt(argc,argv,cmdopts)) != -1)
?? ??? ?switch(optch)
?? ??? ?{
?? ??? ??? ?case 'h':?? ??? ?/* -h for help */
?? ??? ??? ??? ?usage();
?? ??? ??? ??? ?return rc;
?? ??? ??? ??? ?
??? ??? ??? case 'a':??? ??? /* -a quote_server */
??? ??? ??? ??? cmdopt_a = optarg;
??? ??? ??? ??? break;

??? ??? ??? case 'b':??? ??? /* -b broadcast_addr */
??? ??? ??? ??? cmdopt_b = optarg;
??? ??? ??? ??? break;

??? ??? ??? default:
??? ??? ??? ??? /* Option error */
??? ??? ??? ??? rc=1;
??? ??? }

??? /* check for option errors: */
??? if(rc)
??? {
??? ??? usage();
??? ??? return rc;
??? }

??? /*
??? ?* Form the broadcast server address:
??? ?*/
??? bc_len = sizeof bc_addr;??? ??? /* Max len */
??? z = mkaddr(
??? ??? ??? &bc_addr,??? ??? /* Returned addr */
??? ??? ??? &bc_len,??? ??? /* Returned len */
??? ??? ??? cmdopt_b,??? ??? /* Input address */
??? ??? ??? "udp");??? ??? ??? /* UDP protocol */

??? if(z==-1)
??? {
??? ??? msgf('e',"%s: -b %s",
??? ??? ??? ??? strerror(errno),
??? ??? ??? ??? cmdopt_b);
??? ??? return 1;
??? }

??? /*
??? ?* Create a UDP socket to use:
??? ?*/
??? s = socket(PF_INET,SOCK_DGRAM,0);
??? if(s==-1)
??? {
??? ??? msgf('e',"%s: socket(PF_INET,"
??? ??? ??? ??? "SOCK_DGRAM,0)",
??? ??? ??? ??? strerror(errno));
??? ??? return 1;
??? }

??? /*
??? ?* Allow broadcasts on socket s:
??? ?*/
??? z = setsockopt(s,
??? ??? ??? SOL_SOCKET,
??? ??? ??? SO_BROADCAST,
??? ??? ??? &True,
??? ??? ??? sizeof True);
??? if(z==-1)
??? {
??? ??? msgf('e',"%s: setsockopt(SO_BROADCAST)",
??? ??? ??? ??? strerror(errno));
??? ??? return 1;
??? }

??? /*
??? ?* Load tickers form tickers.rc:
??? ?*/
??? if(load(&tickers[0],&ntick,MAX_TICKERS))
??? ??? goto errxit;

??? /*
??? ?* Now monitor the remote quote server:
??? ?*/
??? for(;;)
??? {
??? ??? tn = 0;??? ??? /* Refresh tn */
??? ??? time(&td);??? /* current time */

??? ??? /*
??? ??? ?* Loop for all tickers:
??? ??? ?*/
??? ??? for(x=0;x<ntick;++x)
??? ??? {
??? ??? ??? /*
??? ??? ??? ?* Skip tickers that are either
??? ??? ??? ?* unknown,or are producing parse
??? ??? ??? ?* errors in the returned data:
??? ??? ??? ?*/
??? ??? ??? if(tickers[x].flags & FLG_UNKNOWN
??? ??? ??? ??? ??? || tickers[x].flags & FLG_ERROR)
??? ??? ??? ??? continue;??? ??? /* Ignore this */

??? ??? ??? /*
??? ??? ??? ?* Pick up the earliest "next" time:
??? ??? ??? ?*/
??? ??? ??? if(!tn
??? ??? ??? ??? ??? || tickers[x].next_samp < tn)
??? ??? ??? ??? tn = tickers[x].next_samp;

??? ??? ??? /*
??? ??? ??? ?* if the current time is > than
??? ??? ??? ?* the "next" time,it is time to
??? ??? ??? ?* fetch an update for this ticker:
??? ??? ??? ?*/
??? ??? ??? if(td>=tickers[x].next_samp)
??? ??? ??? {
??? ??? ??? ??? /*
??? ??? ??? ??? ?* Get Quote Update:
??? ??? ??? ??? ?*/
??? ??? ??? ??? z = get_tickinfo(
??? ??? ??? ??? ??? ??? &tickers[x],cmdopt_a);

??? ??? ??? ??? /*
??? ??? ??? ??? ?* comute time for the next
??? ??? ??? ??? ?* update for this ticker:
??? ??? ??? ??? ?*/
??? ??? ??? ??? time(&tickers[x].next_samp);
??? ??? ??? ??? tickers[x].next_samp += tm;

??? ??? ??? ??? /*
??? ??? ??? ??? ?* if the uote fetch was OK,
??? ??? ??? ??? ?* then broadcast its info:
??? ??? ??? ??? ?*/
??? ??? ??? ??? if(!z)
??? ??? ??? ??? ??? broadcast(s,&tickers[x],
??? ??? ??? ??? ??? ??? ??? (struct sockaddr *)&bc_addr,
??? ??? ??? ??? ??? ??? ??? bc_len);
??? ??? ??? }
??? ??? }

??? ??? /*
??? ??? ?* Here the interval between updates is
??? ??? ?* progressively increased to 5 minutes
??? ??? ?* max.This provides a lot of initial
??? ??? ?* acction for demonstration puoposes,
??? ??? ?* without taxing the friendly quote
??? ??? ?* providers if this program is run all
??? ??? ?* day.Abuse will only force the kind
??? ??? ?* providers to change things to break
??? ??? ?* the operation of this program!
??? ??? ?*/
??? ??? if(tm < (time_t)5*60)
??? ??? ??? tm += 5;??? /* progressively increase */

??? ??? /*
??? ??? ?* compute how long we need to snooze.
??? ??? ?* the time to the next event is
??? ??? ?* computed-sleep(3) is called if
??? ??? ?* necessary:
??? ??? ?*/
??? ??? if(!tn)
??? ??? ??? tn = td + tm;
??? ??? if(tn>=td)
??? ??? ??? if((zzz = tn-td))
??? ??? ??? ??? sleep(zzz);
??? }

??? return rc;

??? /*
??? ?* Error Exit:
??? ?*/
errxit:
??? return rc=2;
}

通過get_tickinfo()函數(shù)獲取行情信息

這一部分將會檢測gettkick.c源碼模塊,從而我們可以看到如何通過C代碼來獲取行情信息。在顯示這個模塊之前,我們需要檢測一些會用到的引用結(jié)構(gòu)。下面列出了在這個工程中一些源碼模塊會用到的quotes.h頭文件:
/*
?* quotes.h:
?*
?* Project header file:
?*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <getopt.h>
#include <memory.h>
#include <stdarg.h>
#include <math.h>
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>

/*
?* Default Quote Server:
?*/
#define DFLT_SERVER "finance.yahoo.com:80"

/*
?* Default Broadcast Address:
?*/
#define DFLT_BCAST "127.255.255.255:977"

/*
?* *.CSV Parsing Parameter:
?*/
typedef struct
{
??? char type;??? ??? /* 'S' or 'D' */
??? void *parm;??? ??? /* Ptr to parameter */
} Parm;

/*
?* Timeout on Quote Fetch:
?*/
#define TIMEOUT_SECS 10

/*
?* Ticker load file:
?*/
#define TICKPATH??? "tickers.rc"

/*
?* Maximum number of tickers:
?*/
#define MAX_TICKERS??? 256

/*
?* Ticker length:
?*/
#define TICKLEN??? 8

/*
?* Date Length:
?*/
#define DTLEN??? 10

/*
?* Time field length:
?*/
#define TMLEN??? 7

/*
?* Define TRUE & FALSE if not defined:
?*/
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

/*
?* Ticker Request Structure:
?*/
typedef struct
{
??? char ticker[TICKLEN+1];??? /* Symbol */
??? double last_trade;??? ??? /* Last Price */
??? char *date;??? ??? ??? ??? /* Date */
??? char *time;??? ??? ??? ??? /* Time of Last Trade */
??? double change;??? ??? ??? /* +/- change */
??? double open_price;??? ??? /* Opening Price */
??? double high;??? ??? ??? /* High Price */
??? double low;??? ??? ??? ??? /* Low Price */
??? double volume;??? ??? ??? /* Volume of Trades */
??? int flags;??? ??? ??? ??? /* Server flags */
??? time_t next_samp;??? ??? /* Time of next evt */

} TickReq;

/*
?* Ticker Flags:
?*/
/* Ticker unknown */
#define ??? FLG_UNKNOWN 1

/* Data format error */
#define ??? FLG_ERROR 2

/*
?* External Function References:
?*/
extern int load(TickReq *tick,int *pntick,int nmax);
extern int extract_parms(Parm *plist,short n,char *src);
extern void msgf(char type,const char *format,...);
extern int Connect(const char *addr);
extern int mkaddr(void *addr,
??? ??? int *addrlen,
??? ??? char *str_addr,
??? ??? char *protocol);
extern char *Basename(char *cmd);
extern char *strtick(char *str);
extern int get_tickinfo(TickReq *req,char *addr);
extern void broadcast(
??? ??? int s,TickReq *quote,struct sockaddr *bc_addr,
??? ??? socklen_t bc_len);

有了頭文件,現(xiàn)在我們就可以檢測gettick.c源碼模塊了。
/*
?* gettick.c
?*
?* Get ticker info from inet:
?*/
#include "quotes.h"

/*
?* f is set TRUE when a request
?* for a stock quote has timed
?* out.
?*/
static int f = FALSE;

/*
?* Catch SIGALRM and Timeout:
?*/
static void sig_ALRM(int signo)
{
??? f = TRUE;??? /* Mark timeout */
}

/*
?* Get ticker info:
?*
?* Returns:
?* 0??? success
?* -1??? failed
?*
?* errno:
?*? ETIME??? Timed out
?*? EBADMSG??? Failed data format
?*? other??? Network/system errors
?*/
int get_tickinfo(TickReq *req,char *addr)
{
??? int z,er;??? ??? /* Status,errno */
??? int s;??? ??? ??? /* Socket */
??? int n;??? ??? ??? /* Byte count */
??? char buf[256];??? /* Receive buffer */
??? char *tkr=NULL;??? /* Extracted ticker */
??? struct sigaction
??? ??? sa_new,??? ??? /* New signal action */
??? ??? sa_old;??? ??? /* Saved signal action */
??? Parm parms[9];??? /* Data parse table */

??? /*
??? ?* Initialize parsing parameters.This
??? ?* parameter list will need modification
??? ?* if yahoo or your quote provider uses
??? ?* a different format:
??? ?*/
??? parms[0].type = 'S';??? /* String */
??? parms[0].parm = &tkr;??? /* Ticker name */
??? parms[1].type = 'D';??? /* Double */
??? parms[1].parm = &req->last_trade;
??? parms[2].type = 'S';
??? parms[2].parm = &req->date;
??? parms[3].type = 'S';
??? parms[3].parm = &req->time;
??? parms[4].type = 'D';
??? parms[4].parm = &req->change;
??? parms[5].type = 'D';
??? parms[5].parm = &req->open_price;
??? parms[6].type = 'D';
??? parms[6].parm = &req->high;
??? parms[7].type = 'D';
??? parms[7].parm = &req->low;
??? parms[8].type = 'D';
??? parms[8].parm = &req->volume;

??? /*
??? ?* Initialize to cat SIGALRM:
??? ?*/
??? sa_new.sa_handler = sig_ALRM;
??? sigemptyset(&sa_new.sa_mask);
??? sa_new.sa_flags = 0;
??? sigaction(SIGALRM,&sa_new,&sa_old);

??? /*
??? ?* Connect to finance.yahoo.com:
??? ?*/
??? f = FALSE;
??? alarm(TIMEOUT_SECS);

??? s = Connect(addr);
??? if(s==-1)
??? ??? goto errxit;

??? /*
??? ?* Send GET request:
??? ?* Note: This is subject to change
??? ?* if finance.yahoo.com changes,you
??? ?* will need to adjust this formatting.
??? ?*/
??? sprintf(buf,"GET /d/quotes.csv?"
??? ??? ??? "s=%s"
??? ??? ??? "&f=slldltlclohgv"
??? ??? ??? "&e=.csv/r/n",
??? ??? ??? req->ticker);
??? write(s,buf,strlen(buf));
??? shutdown(s,1);

??? /*
??? ?* Read response with a timeout:
??? ?*/
??? do
??? {
??? ??? z = read(s,buf,sizeof buf);
??? } while(!if && z==-1 && errno==EINTR);

??? er = errno;??? ??? /* Save error */
??? alarm(0);??? ??? /* Diable timeout */
??? close(s);??? ??? /* Close socket */

??? /* Restore the signal action */
??? sigaction(SIGALRM,&sa_old,NULL);

??? if(!f && z>0)
??? ??? n=z;??? /* Read n bytes OK */
??? else
??? {
??? ??? if(f)??? ??? ??? /* Timeout ? */
??? ??? ??? er = ETIME;??? /* Yes - timeout */

??? ??? /*
??? ??? ?* Report error to log:
??? ??? ?*/
??? ??? msgf('e',"%s: Get ticker '%s'",
??? ??? ??? ??? strerror(er),
??? ??? ??? ??? req->ticker);

??? ??? errno = er;??? ??? /* For caller */
??? ???
??? ??? return -1;??? /* Failed */
??? }

??? /*
??? ?* Remove CR,LF,or CRLF */
??? buf[strcspn(buf,"/r/n")]=0;

??? /*
??? ?* check for the unknown ticker case:
??? ?*/
??? if(strstr(buf,"N/A,N/A,N/A,N/A,N/A"))
??? {
??? ??? msgf('e',"Unknown Ticker: '%s'",
??? ??? ??? ??? req->ticker);
??? ??? req->flags |= FLG_UNKNOWN;
??? ??? errno = EBADMSG;??? /* For caller */
??? ??? return -1;??? ??? ??? /* Failed */
??? }

??? /*
??? ?* Parse quote results:
??? ?*/
??? if((z=extract_parms(parms,9,buf))<0)
??? {
??? ??? /*
??? ??? ?* Report failed parse of data */
??? ??? msgf('e',"Field # %d: '%s'",z,buf);
??? ??? req->flags |= FLG_ERROR;
??? ??? errno = EBADMSG;??? /* For caller */
??? ??? return -1;??? ??? ??? /* Failed */

??? }

??? /*
??? ?* Capture the exact case for this ticker
??? ?*/
??? strncpy(req->ticker,tkr,TICKLEN)[TICKLEN]=0;

??? /*
??? ?* Update sample time in entry:
??? ?*/
??? return 0;

??? /*
??? ?* Error Exit:
??? ?*/
errxit:
??? alarm(0);
??? sigaction(SIGALRM,&sa_old,NULL);
??? return -1;
}

下面將會檢測服務(wù)器程序所調(diào)用的broadcast()函數(shù)。

通過broadcast()廣播

服務(wù)器通過調(diào)用名為broadcast()函數(shù)來與本地的網(wǎng)絡(luò)客戶端共享他的得到的信息。這個函數(shù)的代碼如下:
/*
?* bcast.c:
?*
?* Broadcast Ticker Updates
?*/
#include "quotes.h"

void broadcast(
??? ??? int s,??? ??? /* Socket */
??? ??? TickReq *quote,??? /* Quote */
??? ??? struct sockaddr *bc_addr,??? /* addr */
??? ??? socklen_t bc_len)??? /* addr len */
{
??? int z;??? /* status */
??? char buf[2048];??? /* buffer */
??? char *cp = buf;??? /* buf. ptr */
??? int msglen;??? ??? /* message length */

??? /*
??? ?* Format a datagram for broadcast:
??? ?*/
??? strcpy(buf,quote->ticker);
??? cp = buf + strlen(buf) +1;
??? sprintf(cp,"%E",quote->last_trade);
??? cp += strlen(cp)+1;
??? strcpy(cp,quote->date);
??? cp += strlen(cp)+1;
??? strcpy(cp,quote->time);
??? cp += strlen(cp)+1;
??? sprintf(cp,"%E",quote->change);
??? cp += strlen(cp)+1;
??? sprintf(cp,"%E",quote->open_price);
??? cp += strlen(cp)+1;
??? sprintf(cp,"%E",quote->high);
??? cp += strlen(cp)+1;
??? sprintf(cp,"%E",quote->low);
??? cp += strlen(cp)+1;
??? sprintf(cp,"%E",quote->volume);
??? cp += strlen(cp)+1;

??? msglen = cp-buf;

??? /*
??? ?* Broadcast the datagram:
??? ?*/
??? z = sendto(s,buf,msglen,0,bc_addr,bc_len);
??? if(z==-1)
??? ??? msgf('e',"%s:sendto(2)",
??? ??? ??? ??? strerror(errno));
}

檢測客戶端程序

客戶端程序必須將其自身綁定到正在使用的廣播地址,從而可以接收到廣播市場行情信息。mktwatch.c模塊代碼如下:
/*
?* mktwatch.c:
?* Get datagram stock market
?* quotes from central quotes
?* server:
?*/
#include "quotes.h"

/*
?* -b option (broadcast) address:
?*/
static char *cmdopt_b = DFLT_BCAST;

/*
?* Dispaly command useage:
?*/
static void usage(void)
{
??? puts("Usage:/tmktwatch [-b bcast]");
??? puts("where:");
??? puts("/t-b bcast/tBroadcast address");
}

/*
?* Extract ticker information from
?* broadcast datagram packet:
?*/
static int extract(char *dgram,TickRe *tkr)
{
??? char *cp = dgram;
??? memset(tkr,0,sizeof *trk);
??? strncpy(tkr->ticker,dgram,TICKLEN)
??? ??? [TICKLEN]=0;
??? cp += strlen(cp)+1;
??? if(sscanf(cp,"%lE",&tkr->last_trade) != 1)
??? ??? return -1;
??? cp += strlen(cp)+1;
??? tkr->date = cp;
??? cp += strlen(cp)+1;
??? tkr->time = cp;
??? cp += strlen(cp)+1;
??? if(sscanf(cp,"%lE",&tkr->change)!=1)
??? ??? return -1;
??? cp += strlen(cp)+1;
??? if(sscanf(cp,"%lE",&tkr->open_price) != 1)
??? ??? return -1;
??? cp += strlen(cp)+1;
??? if(sscanf(cp,"%lE",&tkr->high) != 1)
??? ??? return -1;
??? cp += strlen(cp)+1;
??? if(sscanf(cp,"%lE",&tkr->low) != 1)
??? ??? return -1;
??? cp += strlen(cp)+1;
??? if(sscanf(cp,"%lE",&tkr->volume) != 1)
??? ??? return -1;

??? return 0;
}

/*
?* Market Watch Main Program:
?*/
int main(int argc,char **argv)
{
??? int rc=0;??? /* command return code */
??? int optch;??? /* option character */
??? int z;??? ??? /* status code */
??? int s;??? ??? /* socket */
??? socklen_t bc_len;??? /* length */
??? struct sockaddr_in bc_addr;??? /* AF_INET */
??? socklen_t a_len;??? /* Address length */
??? struct sockaddr_in adr;??? ??? /* AF_INET */
??? char dgram[2048];??? /* Recv buffer */
??? const int True=TRUE;??? /* True const. */
??? TickReq tkr;??? ??? /* Ticker Data */
??? const char cmdopts[] = "hb:";

??? /*
??? ?* Parse commnd line options:
??? ?*/
??? while((optch = getopt(argc,argv,cmdopt)) != -1)
??? ??? switch(optch)
??? ??? {
??? ??? ??? case 'h':??? /* -h (help) */
??? ??? ??? ??? usage();
??? ??? ??? ??? return rc;

??? ??? ??? case 'b':??? /* -b bc_addr */
??? ??? ??? ??? cmdopt_b = optarg;
??? ??? ??? ??? break;

??? ??? ??? default:???
??? ??? ??? ??? /* option error */
??? ??? ??? ??? rc = 1;

??? ??? }
??? if(rc)
??? {
??? ??? usage();??? /* option errors */
??? ??? return rc;
??? }

??? /*
??? ?* Form broadcast address:
??? ?*/
??? bc_len = sizeof address;
??? z = mkaddr(
??? ??? ??? &bc_addr,??? /* returned addr */
??? ??? ??? &bc_len,??? /* returned len */
??? ??? ??? cmdopt_b,??? /* input address */
??? ??? ??? "udp");??? ??? /* udp protocol */
??? if(z==-1)
??? {
??? ??? fprintf(stderr,
??? ??? ??? ??? "%s: -b %s",
??? ??? ??? ??? strerror(errno),
??? ??? ??? ??? cmdopt_b);
??? ??? return 1;
??? }

??? /*
??? ?* Create a udp socket to read from :
??? ?*/
??? s = socket(PF_INET,SOCK_STREAM,0);
??? if(s==-1)
??? {
??? ??? fprintf(stderr,
??? ??? ??? ??? "%s: socket(2)/n",
??? ??? ??? ??? strerror(errno));
??? ??? return 1;
??? }

??? /*
??? ?* Allow multiple listeners on this
??? ?* broadcast address:
??? ?*/
??? z = setsockopt(s,
??? ??? ??? SOL_SOCKET,
??? ??? ??? SO_REUSEADDR,
??? ??? ??? &True,
??? ??? ??? sizeof True);
??? if(z==-1)
??? {
??? ??? fprintf(stderr,
??? ??? ??? ??? "%s: setsockopt(SO_REUSEADDR)/n",
??? ??? ??? ??? strerror(errno));
??? ??? return 1;
??? }

??? /*
??? ?* Bind to the broadcast address:
??? ?*/
??? z = bind(s,
??? ??? ??? (struct sockaddr *)&bc_addr,bc_len);
??? if(z==-1)
??? {
??? ??? fprintf(stderr,
??? ??? ??? ??? "%s: bind(%s)/n",
??? ??? ??? ??? strerror(errno),
??? ??? ??? ??? cmdopt_b);
??? ??? return 1;
??? }

??? /*
??? ?* Now listen for and process broadcaste
??? ?* stock quotes:
??? ?*/
??? for(;;)
??? {
??? ??? /*
??? ??? ?* wait for a broadcast message:
??? ??? ?*/
??? ??? a_len = sizeof adr;??? /* max addr len */
??? ??? z = recvfrom(s,??? ??? /* socket */
??? ??? ??? ??? dgram,??? ??? /* receiving buffer */
??? ??? ??? ??? sizeof dgram,??? /* max rcv buf size */
??? ??? ??? ??? 0,??? ??? ??? /* flags: no options */
??? ??? ??? ??? (struct sockaddr *)&adr,??? /* Addr */
??? ??? ??? ??? &a_len);??? ??? /* Addr len,in&out */
??? ??? if(z<0)
??? ??? {
??? ??? ??? fprintf(stderr,
??? ??? ??? ??? ??? "%s: recvfrom(2)/n",
??? ??? ??? ??? ??? strerror(errno));
??? ??? ??? break;
??? ??? }

??? ??? /*
??? ??? ?* Extract and report quote:
??? ??? ?*/
??? ??? if(!extract(dgram,&tkr))
??? ??? {
??? ??? ??? printf("%-*s %7.3f %s %7s %+7.3f %7.3f "
??? ??? ??? ??? ??? "%7.3f %7.3f %9.0f/n",
??? ??? ??? ??? ??? TICKLEN,
??? ??? ??? ??? ??? tkr.ticker,
??? ??? ??? ??? ??? tkr.last_trade,
??? ??? ??? ??? ??? tkr.date,
??? ??? ??? ??? ??? tkr.time,
??? ??? ??? ??? ??? tkr.change,
??? ??? ??? ??? ??? tkr.open_price,
??? ??? ??? ??? ??? tkr.high,
??? ??? ??? ??? ??? tkr.low,
??? ??? ??? ??? ??? tkr.volume);
??? ??? ??? fflush(stdout);
??? ??? }
??? }

??? return 0;
}

編譯并運行演示程序

要編譯工程,我們可以使用Makefile文件,如下所示:
$ make
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE qserve.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE csvparse.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE msgf.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE load.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE gettick.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE bcast.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE connect.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE misc.c
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE mkaddr.c
gcc -o qserve qserve.o csvparse.o msgf.o load.o gettick.o bcast.o connect.o
? misc.o mkaddr.o
gcc -c -g -Wall -Wreturn-type -D_GNU_SOURCE mktwatch.c
gcc -o mktwatch mktwatch.o mkaddr.o
$

這會得到兩個可執(zhí)行文件:
qserve可執(zhí)行文件,這是行情服務(wù)器程序
mktwatch可執(zhí)行文件,這是廣播監(jiān)聽客戶端程序

http://www.quecorp.com/series/by_example/

PS:這本是一本在我的機子上放了很久的一本書,連在哪里下載得到的都不再記得了。直到一個偶然的機會試著看了一下,覺得一本很好的書,至少對于我來說是這樣的,所以試著在這里翻譯了一下,希望對大家會有一些小小的幫助。另外,最后的一個例子程序的源代碼可以在上面的網(wǎng)址中得到,但是我自己訪問是卻打不開這個地址,不知道朋友們試了會是什么結(jié)果。

今天是元旦,2008的第一天,在這里蕭易祝朋友們新年快樂~~

最后歡迎大家批評交流~~

?

總結(jié)

以上是生活随笔為你收集整理的Linux Socket学习(十八)--完的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。