Bear在之前介紹過如何利用Socket傳送image
(詳情請參閱:Bear實驗室-如何用WebCam打造監控攝影機?)
但是並沒有對Socket做進一步的解釋 所以在這邊
我們要來簡單介紹一下Socket是如何運作的!!
(詳情請參閱:Bear實驗室-如何用WebCam打造監控攝影機?)
但是並沒有對Socket做進一步的解釋 所以在這邊
我們要來簡單介紹一下Socket是如何運作的!!
Socket 是在UNIX系統下 利用網路通訊的一種接口
一般的程式操作上,大家可以把它當作是對於檔案File I/O的一個操作
基本上也就在於兩個重點: 讀出(Read)與寫入(Write)
也就是如何讀出客戶端(Client)給你的資料,以及如何把資料寫入給你的客戶端兩個重點!
那在進行這些操作以前,基本的步驟就如同上面那張圖
對於一個伺服器端(Server),
你需要進行綁定(bind)連接本地端的網路,取得ip還有設定自己的server要支援哪些port
之後只需要進行監聽(listen)去聽取外部來的request後進行對應的操作
accept等待客戶端傳來的資料,確認有收到request再去讀出以及進行寫入的操作
完成一個動作後把這個連線進行關閉, 基本上這就是一個伺服器的基礎行為!!! (當然還有很多變形操作啦!)
了解這概念後,我們就要開始進入程式碼的認知啦!
以下介紹重要的function
1. int socket(int domain, int type, int protocol)
domain 指明所使用的協定 通常設定為 PF_INET
type 指定socket類型 SOCK_STREAM or SOCK_DGRAM
protocol 通常設定為 0
2. int bind(int sockfd, struct sockaddr *addr, int addrlen)
sockfd 是socket函數的回傳值,
addr 是一個指向包含有本機IP位址及埠號等資訊的sockaddr類型的指針;
addrlen 常被設置為 sizeof(struct sockaddr)
bind 函數 通常只有由server使用
struct sockaddr or struct sockaddr_in (兩者的pointer是可以互相交換的)
是用來保存socket資訊的資料結構
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};下面的結構通常比較常用
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
當bind函數被呼叫的時候 如果設定sin_addr.s_addr = INADDR_ANY
bind函數會自動去搜尋主機的ip位置 然後我們還需要去設定一個未被佔用的port
如果我們設定port為0的話, 電腦會自動搜尋一個未被佔用的port
在設定的時候 我們需要將port, sin_addr利用 htonl, htons轉成網絡字節優先順序
此外 在設定port的時候 我們要注意 要把port的值設定大於1024, 1~1024是預設被保留的
3. int connect(int sockfd, struct sockaddr *serv_addr,int addrlen)
sockfd, addr, addrlen 的設定都跟bind一樣
但是connect函數通常只有在client端呼叫
4. int listen(int socketfd, int backlog)
socketfd跟上面的一樣, backlog則是在queue上等待的數量(等待accept)
大多數的電腦backlog的預設值是20
5. int accept(int sockfd, void *addr, int *addrlen)
sockfd 還是跟上面的一樣
addr直接在宣告的addr後直接拿來用
example: struct sockaddr_in client
在使用時 直接用 &client
addrlen則通常設定為sizeof(struct sockaddr_in)
accept主要是拿來接受通訊協定的開始,這是一種阻塞模式(block mode),
也就是說在沒有收到request以前,執行到accept這隻程式時將會使整個程式暫時停住在那邊
直到收到client的socket後才會繼續往下一行執行!
接下來就是資料傳輸的部分
6.
如果使用的是TCP的協定的話 用send & recv來做傳輸
int send(int sockfd, const void *msg, int len, int flags)
int recv (int sockfd, void *buf, int len, unsigned int flags)
msg 是client 要送的data, len是data的長度, flags預設為0
buf 是放接收到的data的緩衝區 len是該緩衝區的長度
如果是用UDP的話 就要改用sendto & recvfrom 來做傳輸
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen)
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen)
7.
ssize_t write(int fd, const void *buf, size_t count)
ssize_t read(int fd, const void *buf, size_t count)
基本上也是進行讀寫的操作,使用到write或read的話,一般而言屬於linux端裡面接近底層的function
當你的server或專案使用的不是standard library時,使用read/write一般而言不會有太大的問題
這邊提一個Bear的經驗,以前曾經看過使用printf在不同標準凾式庫中產生memory buffer爆掉的問題..
這種狀況改成用write寫到正確的fd就可以通過了!
說了這麼多函數 我們來實際看看怎麼使用吧
下面提供一個從client端輸入string 然後傳送到server端
server端收到以後會回傳給client所收到的東西
同時也會print出收到的data
Server端程式碼
Client端程式碼
其他資料推薦:
1. http://www.tenouk.com/cnlinuxsockettutorials.html
2. Socket programming 筆記
3. http://pws.niu.edu.tw/~ttlee/os.101.1/day/socket/
1. http://www.tenouk.com/cnlinuxsockettutorials.html
2. Socket programming 筆記
3. http://pws.niu.edu.tw/~ttlee/os.101.1/day/socket/
喜歡這篇文章嗎? 趕快加入Takobear粉絲團吧!