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論壇】
|