導航:首頁 > 編程語言 > linux套接字編程實例

linux套接字編程實例

發布時間:2022-06-28 04:22:56

linux C++如何使用套接字

包含頭文件就可以了。socket的api都是C形式的,函數都是聲明好的,頭文件包含好,就能用了。銳英源有專業的視頻,有興趣可以關注下。

Ⅱ linux下c++套接字編程問題

accept(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr));
linux socket不支持 sin_size, unix才支持
推薦你看一下unix socket編程

Ⅲ 求問:linux網路編程套接字如何實現同時實時收發

我現在用了套接字編程,我看書上介紹了TCP
IP之類的協議,那麼套接字已經把這些協議包裝好了,我們還要了解協議干什麼,所以我想,linux網路編程是不是不只是套接字,是不是還有其他的一些東西,求教,真是迷茫了
沒分了
謝謝啊
但是要求
客戶端一直向伺服器發數據,但是伺服器可能偶爾發數據給客戶點,怎麼做才能使兩者不受影響呢?能不能用一個套接字描述符實現呢?
我是想這樣,先建立套接字,然後主線程一隻向伺服器發送數據,然後在建立一個線程,用前面建立的套接字接收伺服器的數據,這樣可行嗎
謝謝了

Ⅳ linux中關於流式套接字編程代碼的解釋,求大神把每行代碼加上注釋,具體一點

伺服器端
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>

intmain(intargc,char*argv)
{
intserver_sockfd,client_sockfd;
intserver_len,client_len;
structsockaddr_inserver_address;
structsockaddr_inclient_address;

//獲得一個socket文件描述符,使用tcpip協議
server_sockfd=socket(AF_INET,SOCK_STREAM,0);
server_address.sin_family=AF_INET;
//設置伺服器端接受任何主機的請求
server_address.sin_addr.s_addr=htonl(INADDR_ANY);
//使用埠9734來接受請求
server_address.sin_port=htons(9734);
server_len=sizeof(server_address);
//綁定socket
bind(server_sockfd,(structsockaddr*)&server_address,server_len);

//監聽連接請求,連接請求隊列大小為5
listen(server_sockfd,5);

while(1)
{
charch;
printf("serverwaiting ");

client_len=sizeof(client_address);

//接受client端的連接請求,如果沒有連接請求,該進程將阻塞
client_sockfd=accept(server_sockfd,(structsockaddr*)&client_address,&client_len);
//讀數據
read(client_sockfd,&ch,1);
ch++;
//寫數據
write(client_sockfd,&ch,1);

//關閉與client端的連接
close(client_sockfd);

}
}

客戶端
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>

intmain(intargc,char*argv)
{
intsockfd;
intlen;
structsockaddr_inaddress;
intresult;
charch='A';

//設置需要連接伺服器的ipport
sockfd=socket(AF_INET,SOCK_STREAM,0);
address.sin_family=AF_INET;
address.sin_addr.s_addr=inet_addr("127.0.0.1");
address.sin_port=htons(9734);
len=sizeof(address);

//連接伺服器
result=connect(sockfd,(structsockaddr*)&address,len);
if(result==-1)
{
perror("oops:client3");
exit(1);
}

//寫數據
write(sockfd,&ch,1);
//讀數據
read(sockfd,&ch,1);
printf("charfromserver=%c ",ch);
//關閉與伺服器的連接
close(sockfd);
exit(0);

}


關於你的問題:
1.簡單的方法,設置socket套接字為非阻塞模式,然後輪詢,並查看是否超時。

或者是使用select poll 等方法 ,設置超時時間。

或者 使用 alarm 信號,都可以。

2..每當接收到一個client 端的連接請求,就開辟一個線程為之服務。

或者使用select poll epoll等方法,推薦這個。

3.你是指的是處理信號嗎?這個有專門的函數 signal 可以處理。

Ⅳ 求linux socket網路編程代碼

Linux是多任務的操作系統,可在運行在Intel 80386及更高檔次的PC機、ARMS、MIPS和PowerPC等多種計算機平台,已成為應用廣泛、可靠性高、功能強大的計算機操作系統,Linux具有內核小、效率高、源代碼開放等優點,還內含了TCP/IP網路協議,很適合在伺服器領域使用,而伺服器主要用途之一就是進行網路通信,隨著計算機辦公自動化處理技術的應用與推廣,網路的不斷普及,傳統的紙張式文件傳輸方式已經不再適合發展的需要,人們更期待一種便捷、高效、環保、安全的網路傳輸方式.

協議概述TCP/IP即傳輸控制協議/網路協議[1](Transmission Control Protocol/Internet Protocol),是一個由多種協議組成的協議族,他定義了計算機通過網路互相通信及協議族各層次之間通信的規范,圖1描述了Linux對IP協議族的實現機制[2]。

Linux支持BSD的套接字和全部的TCP/IP協議,是通過網路協議將其視為一組相連的軟體層來實現的,BSD套接字(BSD Socket)由通用的套接字管理軟體支持,該軟體是INET套接字層,用來管理基於IP的TCP與UDP埠到埠的互聯問題,從協議分層來看,IP是網路層協議,TCP是一個可靠的埠到埠的傳輸層協議,他是利用IP層進行傳接報文的,同時也是面向連接的,通過建立一條虛擬電路在不同的網路間傳輸報文,保證所傳輸報文的無丟失性和無重復性。用戶數據報文協議(User Datagram Protocol,UDP)也是利用IP層傳輸報文,但他是一個非面向連接的傳輸層協議,利用IP層傳輸報文時,當目的方網際協議層收到IP報文後,必須識別出該報文所使用的上層協議(即傳輸層協議),因此,在IP報頭上中,設有一個"協議"域(Protocol)。通過該域的值,即可判明其上層協議類型,傳輸層與網路層在功能說的最大區別是前者提供進程通信能力,而後者則不能,在進程通信的意義上,網路通信的最終地址不僅僅是主機地址,還包括可以描述進程的某種標識符,為此,TCP/UDP提出了協議埠(Protocol Port)的概念,用於標識通信的進程,例如,Web伺服器進程通常使用埠80,在/etc/services文件中有這些注冊了的埠地址。

對於TCP傳輸,傳輸節點間先要建立連接,然後通過該連接傳輸已排好序的報文,以保證傳輸的正確性,IP層中的代碼用於實現網際協議,這些代碼將IP頭增加到傳輸數據中,同時也把收到的IP報文正確的傳送到TCP層或UDP層。TCP是一個面向連接協議,而UDP則是一個非面向連接協議,當一個UDP報文發送出去後,Linux並不知道也不去關心他是否成功地到達了目的的主機,IP層之下,是支持所有Linux網路應用的網路設備層,例如點到點協議(Point to Point Protocol,PPP)和乙太網層。網路設備並非總代表物理設備,其中有一些(例如回送設備)則是純粹的軟體設備,網路設備與標準的Linux設備不同,他們不是通過Mknod命令創建的,必須是底層軟體找到並進行了初始化之後,這些設備才被創建並可用。因此只有當啟動了正確設置的乙太網設備驅動程序的內核後,才會有/dev/eth0文件,ARP協議位於IP層和支持地址解析的協議層之間。

網路通信原理所有的網路通信就其實現技術可以分為兩種,線路交換和包交換,計算機網路一般採用包交換,TCP使用了包交換通信技術,計算機網路中所傳輸的數據,全部都以包(Packet)這個單位來發送,包由"報頭"和"報文"組成,結構如圖2所示,在"報頭"中記載有發送主機地址,接收主機地址及與報文內容相關的信息等,在"報文"中記載有需要發送的數據,網路中的每個主機和路由器中都有一個路由定址表,根據這個路由表,包就可以通過網路傳送到相應的目的主機。

網路通信中的一個非常重要的概念就是套接字(Socket)[3,4],簡單地說,套接字就是網路進程的ID,網路通信歸根到底是進程的通信,在網路中,每個節點有一個網路地址(即IP地址),兩個進程通信時,首先要確定各自所在網路節點的網路地址,但是,網路地址只能確定進程所在的計算機,而一台計算機上可能同時有多個網路進程,還不能確定到底是其中的哪個進程,由此套接字中還要有其他的信息,那就是埠號(Port),在一台計算機中,一個埠一次只能分配給一個進程,即埠號與進程是一一對應的關系,所以,埠號和網路地址就能唯一地確定Internet中的一個網路進程。可以認為:套接字=網路地址+埠號系統調用一個Socket()得到一個套接字描述符,然後就可以通過他進行網路通信了。

套接字有很多種類,最常用的就有兩種;流式套接字和數據報套接字。在Linux中分別稱之為"SOCK_STREAM"和"SOCK_DGRAM)"他們分別使用不同的協議,流式套接字使用TCP協議,數據報套接字使用UDP協議,本文所使用的是流式套接字協議。

網路通信原理在文件傳輸程序設計中的應用網路上的絕大多數通信採用的都是客戶機/伺服器機制(Client/Server),即伺服器提供服務,客戶是這些服務的使用者,伺服器首先創建一個Socket,然後將該Socket與本地地址/埠號綁定(Bind()),成功之後就在相應的Socket上監聽(Listen()) 。當Accept()函數捕捉到一個連接服務(Connect())請求時,接受並生成一個新的Socket,並通過這個新的Socket與客戶端通信,客戶端同樣也要創建一個Socket,將該Socket與本地地址/埠號綁定,還需要指定伺服器端的地址與埠號,隨後向伺服器端發出Connect(),請求被伺服器端接受後,可以通過Socket與伺服器端通信。

TCP是一種面向連接的、可靠的、雙向的通信數據流,說他可靠,是因為他使用3段握手協議傳輸數據,並且在傳輸時採用"重傳肯定確認"機制保證數據的正確發送:接收端收到的數據後要發出一個肯定確認,而發送端必須要能接受到這個肯定信號,否則就要將數據重發。在此原理基礎之上,設計了基於Linux操作系統下TCP/IP編程實現文件傳輸的實例。我們採用客戶機/伺服器模式通信時,通信雙方發送/接收數據的工作流程如圖3所示。

文件傳輸就是基於客戶機/伺服器模型而設計的,客戶機和伺服器之間利用TCP建立連續,因文件傳輸是一個互動式會話系統,客戶機每次執行文件傳輸,都需要與伺服器建立控制連接和數據連接,其中控制連接負責傳輸控制信息、利用控制命令、客戶機可以向伺服器提出無限次的請求,客戶機每次提出的請求,伺服器與客戶機建立一個數據連接,進行實際的數據傳輸,數據傳輸完畢後,對應的數據連接被清除,控制連接依然保持,等待客戶機發出新的傳輸請求,直到客戶機撤銷控制連接,結束會話。

當進行文件傳輸時,首先向伺服器發出連接請求,伺服器驗證身份後,與客戶端建立連接,雙方進入會話狀態,這時只要客戶端向伺服器端發出數據連接請求,建立起數據連接後,雙方就進入數據傳輸狀態,數據傳輸完畢後,數據連接被撤銷,如此循環反復,直到會話結束,從而實現將文件從伺服器端傳輸至客戶機端。

文件傳輸程序設計流程[5,客戶端的TCP應用程序流程(1)先用Socket()創建本地套介面,給伺服器端套介面地址結構賦值。

(2)用Connect()函數使本地套介面向伺服器端套介面發出建立連接請求,經3次握手建立TCP連接。

(3)用Read()函數讀取所要接收的文件名以及存放在內存里的文件內容。

(4)用Open()函數打開客戶端新建立的目標文件,如果沒有建立,該函數會自動生成目標文件,等待存放文件內容。

(5)最後用Write()函數將讀取的文件內容存放在新的目標文件中,以實現伺服器端向客戶端的文件傳輸。

(6)通信結束,用Close()關閉套介面,停止接收文件。

伺服器端的TCP應用程序流程(1)先用Open()函數打開等待傳輸的可讀文件;(2)用Socket()創建套介面,並給套介面地址結構賦值;(3)用Bind()函數綁定套介面;(4)用Listen()函數在該套介面上監聽請求;(5)用Accept()函數接受請求,產生新的套介面及描述字,並與客戶端連接;(6)用Lseek()函數是為了在每次接受客戶機連接時,將用於讀的源文件指針移到文件頭;(7)用Read()函數讀取一定長度的源文件數據;(8)最後用Write()函數將讀取的源文件數據存放在內存中,以便客戶端讀取;(9)傳輸完畢時,用Close()關閉所有進程,結束文件傳輸。

結語Linux操作系統在網路應用方面具有很強的開發潛力,同時Linux也是可靠性、安全性非常高的系統,因此在基於TCP/IP網路通信的研究與開發中,通常選用Linux操作系統作為開發平台

Ⅵ linux 中tcp套接字編程實例中出現Bind() error :Address already in use 怎樣解決

換一個埠,或者把正在佔用這個埠的程序幹掉。

Ⅶ linux 中TCP套接字編程實例 顯示Bind() error:address already in use 怎麼辦 用netstat -nat 查看後結果

你所使用的套接字已被佔用,在Bind()之前你是否申請了套接字,或者申請之後已經被佔用,Bind所使用的套接字來源很重要,可以查查。再有就是linux裡面你的程序如果非法退出或者沒有使用close釋放套接字,在程序結束以後系統會延時自動釋放套接字資源,但是要等幾分鍾,之後你就可以重新使用了。

Ⅷ linux 下套接字socket編程,求代碼,實在寫不出來。總出現各種各樣的錯誤,

linux 編程一般都不會直接空敲代碼的,比較熟練的程序員也要經常查man page的。所以,要有信心,有恆心。從基礎做起,多看書,多練習。
你說的程序其實挺簡單的,寫一個,比較ugly,僅給樓主練習作參考。
tcpclient.c
#include "debug.h"

int main (int argc, char *argv[])
{
int cfd;
char buf[BUFSIZE];
struct sockaddr_in addr;
int addrlen = sizeof (addr);
int data[DATALEN] = { 0 };
int recv_data[DATALEN] = { 0 };
int i, j, n, average = 0;

if (argc < 2)
{
printf ("usage : %s server_ip server_port", argv[0]);
exit (0);
}
cfd = socket (PF_INET, SOCK_STREAM, 0);
check_err ("socket", cfd);

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr (argv[1]);
addr.sin_port = htons (atoi (argv[2]));
connect (cfd, (struct sockaddr *)&addr, addrlen);
check_err ("socket", cfd);

for (i = 0; i < DATALEN; i++)
data[i] = i + 50;
for (i = 0; i < DATALEN; i++)
{
sprintf (buf, "%d", data[i]);
check_err ("write", write (cfd, buf, sizeof (buf)));
}
i = j = 0;
for (i = 0; i < DATALEN && read (cfd, buf, sizeof (buf)) > 0; i++)
{
if ((n = atoi (buf)) > 100)
{
recv_data[j++] = n;
printf ("recv data : %d\n", recv_data[i]);
average += n;
}
else
break;
}
average /= j;
printf ("\naverage = %d\n", average);

close (cfd);
return 0;
}

tcpserver.c
#include "debug.h"

int main (int argc, char *argv[])
{
int ret;
int sfd;
int cfd;
char buf[BUFSIZE];
struct sockaddr_in addr;
int len = sizeof (addr);
int recv_data[DATALEN] = {0};
int i, j, n;

if (argc < 2)
{
printf ("usage : %s port\n", argv[0]);
exit (-1);
}
sfd = socket (PF_INET, SOCK_STREAM, 0);
check_err ("socket", errno);

addr.sin_family = AF_INET;
addr.sin_port = htons (atoi (argv[1]));
addr.sin_addr.s_addr = INADDR_ANY;
bind (sfd, (struct sockaddr *)&addr, len);
check_err ("bind", errno);

listen (sfd, BACKLOG);
check_err ("listen", errno);

i = j = n = 0;
if ((cfd = accept (sfd, (struct sockaddr *)&addr, &len)) > 0)
{
for (i = 0; i < DATALEN && read (cfd, buf, BUFSIZE) > 0; i++)
{
if ((n = atoi (buf)) > 100)
recv_data[j++] = n;
}
}
for (i = 0; i < j; i++)
{
sprintf (buf, "%d", recv_data[i]);
check_err ("write", write (cfd, buf, sizeof(buf)));
}
check_err ("write", write (cfd, "0", sizeof(buf)));

close (cfd);
return 0;
}

debug.h
#ifndef _DEBUG_H
#define _DEBUG_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#define BACKLOG 10 // for max listen fds
#define DATALEN 100
#define BUFSIZE 16

#define check_err(name, ret) \
if (ret < 0) \
{ \
perror (name); \
exit (-1); \
}
#define check_null(name, ret) \
if (if ret == NULL) \
{ \
perror (name); \
exit (-1); \
}

#endif
編譯後,先運行server端,然後運行client端,可以看到server發回的數據,及平均值。
網路知道不能貼代碼,排版很差,將就可以看吧,有問題追問。

Ⅸ Linux編程問題,有關本地域套接字和EPOLL的

我寫的一個可運行的本地域socket的例子,監聽端的服務地址為絕對路徑。例如/tmp/ssss.socket
void *lfs_dispatcher_thread_fn (void *arg)
{
struct sockaddr_in clientaddr;
int fdmax;
int newfd;
char buf[1024];
int nbytes;
int addrlen;
int ret;
int epfd = -1;
int res = -1;
struct epoll_event ev;
int index = 0;
int listen_fd, client_fd = -1;
struct sockaddr_un srv_addr;

listen_fd = socket (AF_UNIX, SOCK_STREAM, 0);
if (listen_fd < 0)
{
perror ("cannot create listening socket");
}
else
{
srv_addr.sun_family = AF_UNIX;
strncpy (srv_addr.sun_path, UNIX_DOMAIN,
sizeof (srv_addr.sun_path) - 1);
unlink (UNIX_DOMAIN);
ret =
bind (listen_fd, (struct sockaddr *) &srv_addr,
sizeof (srv_addr));
if (ret == -1)
{
lfs_printf ("cannot bind server socket");
lfs_printf ("srv_addr:%p", &srv_addr);
close (listen_fd);
unlink (UNIX_DOMAIN);
exit (1);
}
}

ret = listen (listen_fd, 1);
if (ret == -1)
{
perror ("cannot listen the client connect request");
close (listen_fd);
unlink (UNIX_DOMAIN);
exit (1);
}
chmod (UNIX_DOMAIN, 00777); //設置通信文件許可權

fdmax = listen_fd; /* so far, it's this one */

events = calloc (MAX_CON, sizeof (struct epoll_event));
if ((epfd = epoll_create (MAX_CON)) == -1)
{
perror ("epoll_create");
exit (1);
}
ev.events = EPOLLIN;
ev.data.fd = fdmax;
if (epoll_ctl (epfd, EPOLL_CTL_ADD, fdmax, &ev) < 0)
{
perror ("epoll_ctl");
exit (1);
}
//time(&start);
for (;;)
{
res = epoll_wait (epfd, events, MAX_CON, -1);
client_fd = events[index].data.fd;
for (index = 0; index < MAX_CON; index++)
{
if (client_fd == listen_fd)
{
addrlen = sizeof (clientaddr);
if ((newfd =
accept (listen_fd,
(struct sockaddr *) &clientaddr,
(socklen_t *) & addrlen)) == -1)
{
perror ("Server-accept() error lol!");
}
else
{
// lfs_printf("Server-accept() is OK...\n");
ev.events = EPOLLIN;
ev.data.fd = newfd;
if (epoll_ctl
(epfd, EPOLL_CTL_ADD, newfd, &ev) < 0)
{
perror ("epoll_ctl");
exit (1);
}
}
break;
}
else
{
if (events[index].events & EPOLLHUP)
{
// lfs_printf ("find event");
if (epoll_ctl
(epfd, EPOLL_CTL_DEL, client_fd, &ev) < 0)
{
perror ("epoll_ctl");
}
close (client_fd);
break;
}
if (events[index].events & EPOLLIN)
{
/* going to recv data
*/
if ((nbytes =
recv (client_fd, buf, 1024, 0)) <= 0)
{
if (nbytes == 0)
{
}
else
{
lfs_printf ("recv() error lol! %d",
client_fd);
perror ("");
}

if (epoll_ctl
(epfd, EPOLL_CTL_DEL, client_fd,
&ev) < 0)
{
perror ("epoll_ctl");
}
close (client_fd);
}
else
{

// lfs_printf ("nbytes=%d,recv %s,%c", nbytes,
// buf, buf[6]);
process_request (buf, client_fd);
memset (buf, 0, 4);

}
break;
}
}
}
}
return 0;
}

Ⅹ 求前輩指教。linux的套接字編程,這個程序運行,我照著源碼敲,出現一堆錯誤。用的是vim和gcc

再啟動時會出現:
Bind(): Address already in use
的錯誤提示,並導致程序直接退出;

$netstat -an |grep 8080



$ps aux |grep 8080
都還能看到剛才用Ctrl+C「強制結束」了的進程,埠還是使用中,
只能用kill結束進程,才能收回埠,很是麻煩。
在代碼中添加:
int optval;

optval = 1;

ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
可以解決這問題。
在網上查到的更好的解釋如下:
http://www.ibm.com/developerworks/cn/linux/l-sockpit/
在 4.2 BSD UNIX® 操作系統中首次引入,Sockets API 現在是任何操作系統的標准特性。
事實上,很難找到一種不支持 Sockets API 的現代語言。
該 API 相當簡單,但新的開發人員仍然會遇到一些常見的隱患。
本文識別那些隱患並向您顯示如何避開它們。

隱患 1.忽略返回狀態
第一個隱患很明顯,但它是開發新手最容易犯的一個錯誤。
如果您忽略函數的返回狀態,當它們失敗或部分成功的時候,您也許會迷失。
反過來,這可能傳播錯誤,使定位問題的源頭變得困難。
捕獲並檢查每一個返回狀態,而不是忽略它們。
考慮清單 1 顯示的例子,一個套接字 send 函數。

清單 1. 忽略 API 函數返回狀態

int status, sock, mode;

/* Create a new stream (TCP) socket */
sock = socket( AF_INET, SOCK_STREAM, 0 );
...
status = send( sock, buffer, buflen, MSG_DONTWAIT );
if (status == -1)
{
/* send failed */
printf( "send failed: %s\n", strerror(errno) );
}
else
{
/* send succeeded -- or did it? */
}

清單 1 探究一個函數片斷,它完成套接字 send 操作(通過套接字發送數據)。
函數的錯誤狀態被捕獲並測試,但這個例子忽略了send 在無阻塞模式(由 MSG_DONTWAIT 標志啟用)下的一個特性。
send API 函數有三類可能的返回值:
如果數據成功地排到傳輸隊列,則返回 0。
如果排隊失敗,則返回 -1(通過使用 errno 變數可以了解失敗的原因)。
如果不是所有的字元都能夠在函數調用時排隊,則最終的返回值是發送的字元數。
由於 send 的 MSG_DONTWAIT 變數的無阻塞性質,
函數調用在發送完所有的數據、一些數據或沒有發送任何數據後返回。
在這里忽略返回狀態將導致不完全的發送和隨後的數據丟失。

隱患 2.對等套接字閉包
UNIX 有趣的一面是您幾乎可以把任何東西看成是一個文件。
文件本身、目錄、管道、設備和套接字都被當作文件。
這是新穎的抽象,意味著一整套的 API 可以用在廣泛的設備類型上。
考慮 read API 函數,它從文件讀取一定數量的位元組。
read 函數返回:
讀取的位元組數(最高為您指定的最大值);
或者 -1,表示錯誤;
或者 0,如果已經到達文件末尾。
如果在一個套接字上完成一個 read 操作並得到一個為 0 的返回值,這表明遠程套接字端的對等層調用了 close API 方法。
該指示與文件讀取相同 —— 沒有多餘的數據可以通過描述符讀取(參見 清單 2)。

清單 2.適當處理 read API 函數的返回值

int sock, status;
sock = socket( AF_INET, SOCK_STREAM, 0 );
...
status = read( sock, buffer, buflen );
if (status > 0)
{
/* Data read from the socket */
}
else if (status == -1)
{
/* Error, check errno, take action... */
}
else if (status == 0)
{
/* Peer closed the socket, finish the close */
close( sock );
/* Further processing... */
}

同樣,可以用 write API 函數來探測對等套接字的閉包。
在這種情況下,接收 SIGPIPE 信號,或如果該信號阻塞,write 函數將返回 -1 並設置 errno 為 EPIPE。

隱患 3.地址使用錯誤(EADDRINUSE)
您可以使用 bind API 函數來綁定一個地址(一個介面和一個埠)到一個套接字端點。
可以在伺服器設置中使用這個函數,以便限制可能有連接到來的介面。
也可以在客戶端設置中使用這個函數,以便限制應當供出去的連接所使用的介面。
bind 最常見的用法是關聯埠號和伺服器,並使用通配符地址(INADDR_ANY),它允許任何介面為到來的連接所使用。
bind 普遍遭遇的問題是試圖綁定一個已經在使用的埠。
該陷阱是也許沒有活動的套接字存在,但仍然禁止綁定埠(bind 返回EADDRINUSE),
它由 TCP 套接字狀態 TIME_WAIT 引起。
該狀態在套接字關閉後約保留 2 到 4 分鍾。
在 TIME_WAIT 狀態退出之後,套接字被刪除,該地址才能被重新綁定而不出問題。
等待 TIME_WAIT 結束可能是令人惱火的一件事,
特別是如果您正在開發一個套接字伺服器,就需要停止伺服器來做一些改動,然後重啟。
幸運的是,有方法可以避開 TIME_WAIT 狀態。可以給套接字應用 SO_REUSEADDR 套接字選項,以便埠可以馬上重用。
考慮清單 3 的例子。
在綁定地址之前,我以 SO_REUSEADDR 選項調用 setsockopt。
為了允許地址重用,我設置整型參數(on)為 1 (不然,可以設為 0 來禁止地址重用)。

清單 3.使用 SO_REUSEADDR 套接字選項避免地址使用錯誤

int sock, ret, on; struct sockaddr_in servaddr; /* Create a new stream (TCP) socket */ sock = socket( AF_INET, SOCK_STREAM, 0 ):
/* Enable address reuse */
on = 1;
ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

/* Allow connections to port 8080 from any available interface */
memset( &servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
servaddr.sin_port = htons( 45000 );

/* Bind to the address (interface/port) */
ret = bind( sock, (struct sockaddr *)&servaddr, sizeof(servaddr) );

在應用了 SO_REUSEADDR 選項之後,bind API 函數將允許地址的立即重用。

隱患 4.發送結構化數據
套接字是發送無結構二進制位元組流或 ASCII 數據流(比如 HTTP 上的 HTTP 頁面,或 SMTP 上的電子郵件)的完美工具。但是如果試圖在一個套接字上發送二進制數據,事情將會變得更加復雜。
比如說,您想要發送一個整數:您可以肯定,接收者將使用同樣的方式來解釋該整數嗎?
運行在同一架構上的應用程序可以依賴它們共同的平台來對該類型的數據做出相同的解釋。
但是,如果一個運行在高位優先的 IBM PowerPC 上的客戶端發送一個 32 位的整數到一個低位優先的 Intel x86,
那將會發生什麼呢?
位元組排列將引起不正確的解釋。
位元組交換還是不呢?
Endianness 是指內存中位元組的排列順序。高位優先(big endian) 按最高有效位元組在前排列,然而 低位優先(little endian) 按照最低有效位元組在前排序。
高位優先架構(比如 PowerPC®)比低位優先架構(比如 Intel® Pentium® 系列,其網路位元組順序是高位優先)有優勢。這意味著,對高位優先的機器來說,在 TCP/IP 內控制數據是自然有序的。低位優先架構要求位元組交換 —— 對網路應用程序來說,這是一個輕微的性能弱點。
通過套接字發送一個 C 結構會怎麼樣呢?這里,也會遇到麻煩,因為不是所有的編譯器都以相同的方式排列一個結構的元素。結構也可能被壓縮以便使浪費的空間最少,這進一步使結構中的元素錯位。
幸好,有解決這個問題的方案,能夠保證兩端數據的一致解釋。過去,遠程過程調用(Remote Procere Call,RPC)套裝工具提供所謂的外部數據表示(External Data Representation,XDR)。XDR 為數據定義一個標準的表示來支持異構網路應用程序通信的開發。
現在,有兩個新的協議提供相似的功能。可擴展標記語言/遠程過程調用(XML/RPC)以 XML 格式安排 HTTP 上的過程調用。數據和元數據用 XML 進行編碼並作為字元串傳輸,並通過主機架構把值和它們的物理表示分開。SOAP 跟隨 XML-RPC,以更好的特性和功能擴展了它的思想。參見 參考資料 小節,獲取更多關於每個協議的信息。
回頁首
隱患 5.TCP 中的幀同步假定
TCP 不提供幀同步,這使得它對於面向位元組流的協議是完美的。
這是 TCP 與 UDP(User Datagram Protocol,用戶數據報協議)的一個重要區別。
UDP 是面向消息的協議,它保留發送者和接收者之間的消息邊界。
TCP 是一個面向流的協議,它假定正在通信的數據是無結構的,
如圖 1 所示。

圖 1.UDP 的幀同步能力和缺乏幀同步的 TCP

圖 1 的上部說明一個 UDP 客戶端和伺服器。
左邊的對等層完成兩個套接字的寫操作,每個 100 位元組。
協議棧的 UDP 層追蹤寫的數量,並確保當右邊的接收者通過套接字獲取數據時,它以同樣數量的位元組到達。
換句話說,為讀者保留了寫者提供的消息邊界。
現在,看圖 1 的底部.它為 TCP 層演示了相同粒度的寫操作。
兩個獨立的寫操作(每個 100 位元組)寫入流套接字。
但在本例中,流套接字的讀者得到的是 200 位元組。
協議棧的 TCP 層聚合了兩次寫操作。
這種聚合可以發生在 TCP/IP 協議棧的發送者或接收者中任何一方。
重要的是,要注意到聚合也許不會發生 —— TCP 只保證數據的有序發送。
對大多數開發人員來說,該陷阱會引起困惑。
您想要獲得 TCP 的可靠性和 UDP 的幀同步。
除非改用其他的傳輸協議,比如流傳輸控制協議(STCP),
否則就要求應用層開發人員來實現緩沖和分段功能。

調試套接字應用程序的工具
GNU/Linux 提供幾個工具,它們可以幫助您發現套接字應用程序中的一些問題。
此外,使用這些工具還有教育意義,而且能夠幫助解釋應用程序和 TCP/IP 協議棧的行為。
在這里,您將看到對幾個工具的概述。查閱下面的 參考資料 了解更多的信息。
查看網路子系統的細節
netstat 工具提供查看 GNU/Linux 網路子系統的能力。
使用 netstat,可以查看當前活動的連接(按單個協議進行查看),
查看特定狀態的連接(比如處於監聽狀態的伺服器套接字)和許多其他的信息。
清單 4 顯示了 netstat 提供的一些選項和它們啟用的特性。

清單 4.netstat 實用程序的用法模式

View all TCP sockets currently active $ netstat --tcp View all UDP sockets $ netstat --udp View all TCP sockets in the listening state $ netstat --listening View the multicast group membership information $ netstat --groups Display the list of masqueraded connections $ netstat --masquerade View statistics for each protocol $ netstat --statistics

盡管存在許多其他的實用程序,但 netstat 的功能很全面,
它覆蓋了 route、ifconfig 和其他標准 GNU/Linux 工具的功能。
監視流量
可以使用 GNU/Linux 的幾個工具來檢查網路上的低層流量。
tcpmp 工具是一個比較老的工具,它從網上「嗅探」網路數據包,列印到stdout 或記錄在一個文件中。
該功能允許查看應用程序產生的流量和 TCP 生成的低層流控制機制。
一個叫做 tcpflow 的新工具與tcpmp 相輔相成,
它提供協議流分析和適當地重構數據流的方法,而不管數據包的順序或重發。
清單 5 顯示 tcpmp 的兩個用法模式。

清單 5.tcpmp 工具的用法模式

Display all traffic on the eth0 interface for the local host
$ tcpmp -l -i eth0 // Show all traffic on the network coming from or going to host plato
$ tcpmp host plato // Show all HTTP traffic for host camus
$ tcpmp host camus and (port http) //View traffic coming from or going to TCP port 45000 on the local host
$ tcpmp tcp port 45000

tcpmp 和 tcpflow 工具有大量的選項,包括創建復雜過濾表達式的能力。
查閱下面的 參考資料 獲取更多關於這些工具的信息。
tcpmp 和 tcpflow 都是基於文本的命令行工具。
如果您更喜歡圖形用戶界面(GUI),有一個開放源碼工具 Ethereal 也許適合您的需要。
Ethereal 是一個專業的協議分析軟體,它可以幫助調試應用層協議。
它的插入式架構(plug-in architecture)可以分解協議,
比如 HTTP 和您能想到的任何協議(寫本文的時候共有 637 個協議)。
回頁首
總結
套接字編程是容易而有趣的,但是您要避免引入錯誤或至少使它們容易被發現,
這就需要考慮本文中描述的這 5 個常見的陷阱,並且採用標準的防錯性程序設計實踐。
GNU/Linux 工具和實用程序還可以幫助發現一些程序中的小問題。
記住:在查看實用程序的幫助手冊時候,跟蹤相關的或「請參見」工具。
您也許會發現一個必要的新工具。

閱讀全文

與linux套接字編程實例相關的資料

熱點內容
成都市區建成面積演算法 瀏覽:656
智能家居單片機 瀏覽:93
買男裝用什麼app好 瀏覽:851
文件夾合並了怎麼拆開 瀏覽:256
波段副圖源碼無未來函數 瀏覽:84
livecn伺服器地址 瀏覽:257
程序員這個工作真的很吃香嗎 瀏覽:844
程序員和數學分析師待遇 瀏覽:678
壓縮氣彈簧怎麼拆 瀏覽:321
華為公有雲伺服器添加虛擬ip 瀏覽:209
程序員和運營哪個累 瀏覽:24
抖音安卓信息提示音怎麼設置 瀏覽:454
光速虛擬機的共享文件夾 瀏覽:248
程序員培訓機構發的朋友圈真實性 瀏覽:742
天乾地支簡單演算法 瀏覽:299
下載個壓縮文件 瀏覽:300
普通人電腦關機vs程序員關機 瀏覽:628
米酷建站源碼 瀏覽:115
氫氣app怎麼搜搭配 瀏覽:619
pdf綠盟 瀏覽:505