分类: LINUX
2013-04-15 14:55:57
chapter 7 Socket程式設計
7-1 TCP/IP 簡介
如何以Linux C來設計網路程式
TCP/IP是一個協定,是一組支援網路溝通協定的集合。TCP/IP通訊協定的主要工作,是定義網路傳送時的資料單位,並說明一個資料單位應包含哪些資訊,以便讓接收此資料單位的電腦可以正確的解析訊息。
TCP/IP協定系統分成幾個不同的階層式元件,每一層元件負責一項工作,這個模型稱之為TCP/IP模組架構。
TCP/IP模組架構
一個TCP/IP模組架構包含4個階層,分為網路存取層、網際網路層、傳輸層和應用層。
階層 | 說明 |
網路存取層 | 提供一個與實體網路之間的界面,可讓主機具有與網路連接並發送IP封包的能力。 |
網際網路層 | 提供與硬體無關的邏輯位址,並將資料封包(packet)發送至網路上,並讓它們獨立的到達目的地。在此層中定義[IP協定](internet protocol),此協定為網際網路上的電腦,提供一個共同的定址結構,稱為IP位址。 |
傳輸層 | 為網際網路提供流程控制、錯誤檢查、回報服務,是與網路應用程式間的界面。TCP/IP模組定義二組協定,TCP和UDP。TCP是一個連結協定,透過 TCP可確保接收端收到完整、正確的資料。另一方面,UDP則是一個非連結協定,比TCP快,但可靠度差。 |
應用層 | 為網路除錯、檔案傳輸、遠端控制和網際網路活動提供應用程式,包含FTP、HTTP、SMTP、TELNET、與DNS等功能。 |
7-2 socket基本概念
Linux作業系統使用socket觀念來設計網路程式,在使用TCP和UDP網路程式之前,必須先了解socket基本觀念。
socket 是一種可做雙向資料傳輸的通道,Linux程序可經由此裝置與本地端或是遠端的程序做溝通。
Linux socket分成unix-domain socket和internet-domain socket二種。unix-domain socket又稱為local socket,主要用來與本地端的程序溝通。internet-domain socket用來與遠地端的程序溝通。
7-3 IPv4 socket定址結構
internet domain socket的定址結構可用來儲存IP位址、通訊埠等訊息。
IPv4是目前internet使用的網路定址模式,他的定址結構稱為sockaddr_in,定義如下:
結構:
struct sockaddr_in {
sa_family_t sin_family; /* AF_INET */
unsigned short int sin_port; /* port number */
struct in_addr sin_addr; /* internet address */
};
其中的成員結構說明如下:
結構成員 | 說明 |
sin_family | 用來說明socket所使用的定址模式,在此必須設為AF_INET,表示internet domain的socket。 |
sin_port | 用來表示TCP/IP的port umber,設定sin_port必需使用htons函數作位元排序的動作。 |
sin_addr | 是一個in_addr的結構變數,用來表示ip位址。 |
in_addr結構的定義如下:
struct in_addr {
unsigned long int s_addr;
};
7-4 設定IPv4 socket定址結構
用來存放IP位址的s_addr是一個32位元unsigned integer,但是ip通常會表示為xxx.xxx.xxx.xxx,例如192.168.1.100。可使用inet_addr()位址轉換函數將 192.168.1.100轉換為s_addr所要的32位元unsigned integer。
inet_addr()
用來將xxx.xxx.xxx.xxx格式的ip位址轉換成32位元unsigned integer。格式
#include
#include
#include
unsigned long inet_addr(const char *string);
其中string是一個ip字串,格式為xxx.xxx.xxx.xxx。
位元排序函數
由於不同的CPU在儲存32位元整數時,會將4個位元組由高到低(例:motorola CPU),或由低到高(例:intel CPU)儲存,造成網路處理上的錯誤。因此在儲存TCP/IP的port number時,可以用位元排序函數來消除此問題。
常用的位元排序函數有二個:
格式
#include
unsigned long htonl(unsigned long hostlong);
unsigned short htons(unsigned short hostshort);
其中的引數說明
引數 | 說明 |
hostlong | 欲轉換的整數為長整數 |
hotshort | 欲轉換的整數為短整數 |
範例
struct sockaddr_in adr_srvr;
adr_srvr.sin_addr.s_addr=inet_addr("192.168.1.100");
adr_srvr.sin_port=htons(8000);
7-5 socket相關函數
socket()
不論是TCP或UDP作為傳輸協定,都要透過socket作資料傳輸,首要工作是先建立socket。要建立socket可用socket()函數。
格式:
include
include
int socket(int domain, int type, protocol);
其中的引數說明:
引數 | 說明 |
domain | 設定為AF_INET表示internet協定 |
type |
連結的型態。 設定為SOCK_STRREAM表示TCP傳輸層協定。 設定為SOCK_DGRAM表示UCP傳輸層協定。 |
protocol | 通訊協定,一般為0,表示自動選擇。 |
傳回值:
成功:傳回socket ID。
失敗:傳回-1。
bind()
將IPv4 socket定址結構連結到所建立的socket,以後當有封包抵達往路介面時,Linux核心便會將這個封包導向到其連結的socket。
格式
#include
int bind(int sockfd, const struct sockaddr *my_addr, size_t adr_len);
其中的引數說明:
引數 | 說明 |
sockfd | socket函數執行後傳回的socket ID |
my_addr | 指向socket sockaddr_in結構的指標,用來存放連結的IPv4定址結構 |
adr_len | struct sockaddr_in結構的長度,可將其指定為sizeof(struct sockaddr_in) |
傳回值
成功:傳回0
失敗:傳回-1
listen()
建立socket,做好bind後,在server端使用listen()函數來通知Linux核心將其設為listening socket,等待client端的連線要 求。
格式:
#include
int listen(int sockfd, int backlog);
其中的引數說明:
引數 | 說明 |
sockfd | socket函數執行後傳回的socket ID |
backlog | 指定最大連線的數量,通常為5 |
傳回值
成功:傳回0
失敗:傳回-1
accept()
當server端接收到client端的連線請求時,就會把連線請求放在連線佇列中,接著server端必須呼叫accept函數來處理並接受佇列中的連線請求。
格式:
#include
int accept(int socketfd, struct sockaddr *addr, socklen_t addrlen);
其中的引數說明:
引數 | 說明 |
sockfd | socket函數執行後傳回的socket ID |
addr | 指向struct sockaddr_in結構的指標,用來存放client端的IP address |
addrlen | 存放client IP address變數的長度,初值為sizeof(struct sockaddr_in),accept()執行成功後會回存實際的client ip address長度。 |
傳回值
成功:傳回client的socket ID,稱為connect socket。
失敗:傳回-1
connect()
client端建立socket後,可用connect()函數向server端要求建立連線,在確定連線後,client端和sever端就能開始互相傳送資料。
格式:
#include
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
其中的引數說明:
引數 | 說明 |
sockfd | socket函數執行後傳回的socket ID |
serv_addr | 指向struct sockaddr_in結構的指標,用來存放server的address1 |
addrlen | struct sockaddr_in結構的長度,可指定為sizeof(struct sockaddr_in) |
傳回值
成功:傳回0
失敗:傳回-1
close()
呼叫close()終止client端和server端的連線。
格式
#include
int clode(int sockfd);
傳回值
成功:傳回0
失敗:傳回-1
7-6 設計tcp網路程式
網路程式會分成client端和server端。
設計TCP client端
1.建立socket(使用socket()函數)
2.向server要求連線(使用connect()函數)
3.若連線成功,使用輸出入函數和server端互傳訊息
4.關閉socket,結束連線(使用close()函數)
設計TCP server端
1.建立socket(使用socket()函數)
2.連結socket(使用bind()函數)
3.開啟listening socket(使用listen()函數)
4.等待client連線要求(使用accept()函數)
5.若連線成功,使用輸出入函數和client端互傳訊息
6.關閉socket,結束連線(使用close()函數)
TCP程式設計流程
特殊的TCP port number
20 | TCP | FTP - data port Official |
21 | TCP | FTP - control (command) port Official |
22 | TCP | SSH (Secure Shell) - used for secure logins, file transfers (scp, sftp) and port forwarding Official |
23 | TCP | Telnet protocol - unencrypted text communications Official |
25 | TCP | SMTP - used for e-mail routing between mailservers E-mails Official |
53 | TCP | DNS (Domain Name System) Official |
80 | TCP | HTTP (HyperText Transfer Protocol) - used for transferring web pages Official |
80 | TCP | HTTP - HTTP listening port Official |
110 | TCP | POP3 (Post Office Protocol version 3) - used for sending/retrieving E-mails |
137 | TCP | NetBIOS NetBIOS Name Service Official |
138 | TCP | NetBIOS NetBIOS Datagram Service Official |
139 | TCP | NetBIOS NetBIOS Session Service Official |
7-7 TCP 輸出入函數
read()
從已經開啟的socket讀取資料
格式
#include
int read(int sockfd, char *buf, int len);
其中的引數說明:
引數 | 說明 |
sockfd | socket函數執行後傳回的socket ID |
buf | 指向字元暫存器的指標,用來存放讀取到的資料 |
len | 欲讀取的字元長度。 |
傳回值
成功:傳回接收的字元數
失敗:傳回-1
write()
將資料寫入已經開啟的socket
格式
#include
int write(int sockfd, char *buf, int len);
其中的引數說明:
引數 | 說明 |
sockfd | socket函數執行後傳回的socket ID |
buf | 指向字元暫存器的指標,用來存放讀取到的資料 |
len | 欲讀取的字元長度。 |
傳回值
成功:傳回寫入的字元數
失敗:傳回-1
recv()
從已經開啟的socket接收資料
格式
#include
#include
int recv(int s, void *buf, int len, unsigned int flags);
其中的引數說明:
ru; y xul4
將資料存到buf指向的記憶體,len為可接收的最大長度,flags一般設為0
引數 | 說明 |
MSG_OOB | 接收以out-of-band送來的資料 |
MSG_PEEK | 遠端socket傳來的資料,不會在接收受刪除 |
MSG_WAITALL | 固定接收len引數指定長度的資料,除非有錯誤或訊號發生 |
MSG_NOSIGNAL | 接收動作不會因SIGPIPE訊號中斷 |
傳回值
成功:傳回接收的字元數
失敗:傳回-1
send()
從已經開啟的socket傳送資料
格式
#include
#include
int send(int s, const void *msg, int len, unsigned int flags);
其中的引數說明:
ru; y xul4
將buf指向的資料經由socket傳送到遠端,len為可傳送的最大長度,flags一般設為0
引數 | 說明 |
MSG_OOB | 接收的資料以out-of-band送出 |
MSG_DONTROUTE | 取消route的查詢 |
MSG_DONTWAIT | 傳送過程不可以被阻斷 |
MSG_NOSIGNAL | 傳送動作不會因SIGPIPE訊號中斷 |
傳回值
成功:傳回傳送的字元數
失敗:傳回-1
7-8 TCP程式設計
目的:
撰寫TCP通訊協定的server端和client端程式。
要求:
1.server端建立socket,連結socket,等待client端連線。
2. client端建立socket,與server連線,連線成功後,將字串str傳給server。
3.server端若接受client端連線請求,則讀取client端傳過來的字串。處理後,將字串結果傳回client端。
4.client端收到結果後,將結果顯示在螢幕。
server端
/* server.c */ #include |
client端
/* client.c */ #include |
程式設計 TCP
server端
#include |
/* 利用socket的TCP client 此程序會連線TCP server,並將鍵盤輸入的字符串傳送給server。 */ #include |
7-9 指令引數列
指令列引數的使用方法,此功能可讓程式在執行時,接受使用者輸入一些字串,作為主程式的參數。
int main(int argc, char *argv[])
{
...
}
其中第一個引數argc會儲存使用者輸入的字串個數,第二個引數argv[]為字串指標陣列,指向使用者輸入字串的內容。例如,在執行client端時輸入下列字串:
./client "A002 音響 15000.00" |
引數argc的值為2,引數argv[0]的值為client,引數argv[1]的值為A002 音響 15000.00。
7-10 設計UDP網路程式
UDP網路程式一樣分成client端程式設計和server端程式設計。
設計UDP client端
使用UDP通訊協定設計client端程式,步驟如下:
1. 建立socket(使用socket()函數)
2.以sendto()函數傳送資料給server端。
3.以recvfrom()函數接收server端傳來的資料。
4.關閉socket(使用close()函數)
設計UDP client端
1. 建立socket(使用socket()函數)
2.連結socket(使用bind()函數)
3.以recvfrom()函數接收client端傳來的資料。
4.以sendto()函數傳送資料給client端。
4.關閉socket(使用close()函數)
UDP函數設計流程
UDP與TCP網路程式設計的比較
由於UDP為非連結,不可信賴的協定,在傳送端和接收端不須建立連線。當UDP server建立socket,連結socket後,client與server便可以透過sendto()函數與recvfrom()函數進行通訊。
特殊的UDP port number
20 | UDP | FTP - data port Official |
21 | UDP | FTP - control (command) port Official |
22 | UDP | SSH (Secure Shell) - used for secure logins, file transfers (scp, sftp) and port forwarding Official |
23 | UDP | Telnet protocol - unencrypted text communications Official |
25 | UDP | SMTP - used for e-mail routing between mailservers E-mails Official |
53 | UDP | DNS (Domain Name System) Official |
67 | UDP | BOOTP (BootStrap Protocol) server; also used by DHCP (Dynamic Host Configuration Protocol) Official |
137 | UDP | NetBIOS NetBIOS Name Service Official |
138 | UDP | NetBIOS NetBIOS Datagram Service Official |
139 | UDP | NetBIOS NetBIOS Session Service Official |
7-11 UDP輸出入函數
sendto()
用來將資料從指定的socket傳給遠端主機,並且指定接收端網路位址。
格式:
#include
#include
int sendto (int s, const void *buf, int len, unsigned flags, const struct sockaddr *to, int tolen);
引數說明如下:
引數 | 說明 |
s | 傳送資料的socket ID |
buf | 暫存器指標,用來存放欲傳送的訊息 |
len | sizeof(buf) |
flags | 一般設為0 |
to | 接收端網路位址 |
tolen | sizeof(to) |
傳回值
成功:傳回傳送的字元數
失敗:傳回-1
recvfrom()
接收遠端主機從socket傳來的資料,並且同時接收傳送端的網路位址
格式:
#include
#include
int recvfrom (int s, void *buf, int len, unsigned flags, struct sockaddr *from, int fromlen);
引數說明如下:
引數 | 說明 |
s | 傳送資料的socket ID |
buf | 暫存器指標,用來存放欲傳送的訊息 |
len | sizeof(buf) |
flags | 一般設為0 |
from | 接收端網路位址 |
fromlen | sizeof(from) |
傳回值
成功:傳回接收的字元數
失敗:傳回-1
7-12 UDP程式設計
server端
/* udp_server.c */ #include |
client端
/* udp_client.c */ #include |