QQRead:http://www.qqread.com/linux/2007/12/y384831.html
本文編的是echo服務器示例程序,當收到客戶端的數據,服務器把數據不經加工地發送給客戶。采用TCP連接,采用端口8080進行設計,在整個過程中主要涉及socket的通信。
首先建立一個 socket,代碼如下:
int socketfd;
socketfd = socket(AF_INET, SOCK_STREAM, 0);
socket函數是我們寫socket程序遇到的第一個函數,它在指定的協議上創建一個socket,它的函數說明如下所示:
#include
int socket ( int AddressFamily, int Type, int Protocol)
其中:AddressFamily參數指定socket操作中所要解釋的網絡地址類型,值為如下之一:
AF_UNIX
表示操作系統文件路徑
AF_INET
表示Internet網絡地址
AF_NS
表示XEROX網絡地址
Type參數表明了通信的語義,即通信連接的方式。參數為如下之一:
SOCK_STREAM
提供穩定可靠的連接,並且是雙向的通信方式,如TCP。
SOCK_DGRAM
提供無連接的數據報通信,如UDP。
SOCK_RAW
提供該問內部網絡協議和網絡接口, 只有root用戶才可以使用些協議。
返回值:成功則返socket描述符,出錯則返回-1,可通過errno代碼進行查看錯誤原因。
再次,把socket綁定到本機上,代碼如下:
struct sockaddr_in sa;
bzero(&sa, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(EHCO_PORT);
sa.sin_addr.s_addr = htons(INADDR_ANY);
bzero(&(sa.sin_zero), 8);
if(bind(socketfd, (struct sockaddr *)&sa, sizeof(sa))!= 0)
{
printf("bind failed ");
printf("errno=%d ", errno);
exit(1);
}
else
{
printf("bind successfully ");
}上面的代碼中,定義一個scokaddr_in 結構體變量sa,然後填機服務所要開通的端口號和地址。
sa.sin_family = AF_INET;
>表明地址類型
sa.sin_port = htons(EHCO_PORT);
>端口號為8080
sa.sin_addr.s_addr = htons(INADDR_ANY);
>表明綁定在本機
然後利用bind函數,把剛才已建立的socket作為參數,綁定起來。
綁定完成後,服務器要偵聽客戶端的連接,因此首先要完成偵聽設置這一過程,由listen函數實現,代碼如下:
if(listen(socketfd ,MAX_CLIENT_NUM) != 0)
...{
printf("listen error ");
exit(1);
}
else
...{
printf("listen successfully ");
}
listen(socketfd, MAX_CLIENT_NUM)表明在socketfd上偵聽,其中客戶個數最大值為MAX_CLIENT_NUM。
完成偵聽後,可以讓客戶與服務器進行連接了。服務想獲得客戶的請求,則需要通過 accept函數來獲得。同時,需要采用一個sockaddr_in結構體來獲得客戶的信息。代碼如下:
int clientfd;
struct sockaddr_in clientAdd;
char buff[101];
socklen_t len = sizeof(clientAdd);
int closing =0;
while( closing == 0 && (clientfd = accept(socketfd, (struct sockaddr *)&clientAdd, &len)) >0 )
{
int n;
while((n = recv(clientfd,buff, 100,0 )) > 0)
{
printf("number of receive bytes = %d ", n);
write(STDOUT_FILENO, buff, n);
send(clientfd, buff, n, 0);
buff[n] = '';
if(strcmp(buff, "quit ") == 0)
{
break;
}
else if(strcmp(buff, "close ") == 0)
{
//server closing
closing = 1;
printf("server is closing ");
break;
}
}
close(clientfd);
}
其中clientfd為客戶的socket,在服務器端,每接受一個客戶連接,都會返回一個客戶的socket描述符,服務器根據它與客戶進行通信。clientAdd為客戶地址信息的結構體,在accept函數中完成對它的填充,可依此得到客戶的地址信息。
while( closing == 0 && (clientfd = accept(socketfd, (struct sockaddr *)&clientAdd, &len)) >0 )
等待第一個客戶,當第一個客戶的請求來到服務器後,該函數會返回,clientfd為客戶的socket描述符。
接著進行通信
while((n = recv(clientfd,buff, 100,0 )) > 0)
等待客戶的數據,收到數據後,在標准輸入出顯示接收的數據信息,並把它發送回給客戶:send(clientfd, buff, n, 0);
在這裡,我們采用簡單的命令對通信進行控制,quit表示客戶要結束通信過程,而 close表示客戶請求關閉服務器。關閉只需使用 close函數即可完成。
下面是完整的代碼:
#include
#include
#include
#include
#include
#define EHCO_PORT 8080
#define MAX_CLIENT_NUM 10
int main()
{
int socketfd;
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if(socketfd == -1)
{
printf("errno=%d ", errno);
exit(1);
}
else
{
printf("socket create successfully ");
}
struct sockaddr_in sa;
bzero(&sa, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(EHCO_PORT);
sa.sin_addr.s_addr = htons(INADDR_ANY);
bzero(&(sa.sin_zero), 8);
if(bind(socketfd, (struct sockaddr *)&sa, sizeof(sa))!= 0)
{
printf("bind failed ");
printf("errno=%d ", errno);
exit(1);
}
else
{
printf("bind successfully ");
}
//listen
if(listen(socketfd ,MAX_CLIENT_NUM) != 0)
{
printf("listen error ");
exit(1);
}
else
{
printf("listen successfully ");
}
int clientfd;
struct sockaddr_in clientAdd;
char buff[101];
socklen_t len = sizeof(clientAdd);
int closing =0;
while( closing == 0 && (clientfd = accept(socketfd, (struct sockaddr *)&clientAdd, &len)) >0 )
{
int n;
while((n = recv(clientfd,buff, 100,0 )) > 0)
{
printf("number of receive bytes = %d ", n);
write(STDOUT_FILENO, buff, n);
send(clientfd, buff, n, 0);
buff[n] = '';
if(strcmp(buff, "quit ") == 0)
{
break;
}
else if(strcmp(buff, "close ") == 0)
{
//server closing
closing = 1;
printf("server is closing ");
break;
}
}
close(clientfd);
}
close(socketfd);
return 0;
}
經過cc編譯後,即可運行。在這裡我們寫的程序是服務器程序,要想完成通信,也得寫一個客戶端程吧???
呵呵,我們先把客戶端的程序放下來,先測測我們服務器程序吧。在這裡,我們使用 telnet充當客戶端進行測試,telnet可以說是一個很好的客戶端程序。呵呵:
本機IP為192.168.0.69,整個通信過程如下:
linyongting@linyongting:~$ telnet 192.168.0.69 8080
Trying 192.168.0.69...
Connected to 192.168.0.69.
Escape character is '^]'.
hello! This is my first packet.Can you reply to me?
hello! This is my first packet.Can you reply to me?
Ohh, U did it!
Ohh, U did it!
see U next time!!!
see U next time!!!
quit
quit
Connection closed by foreign host.
linyongting@linyongting:~$ telnet 192.168.0.69 8080
Trying 192.168.0.69...
Connected to 192.168.0.69.
Escape character is '^]'.
close
close
Connection closed by foreign host.
上面連接了兩次,第一次時,與服務器通信3次,每次發信息過去後,都收到與發出來一模一樣的信息。當用戶輸入quit的時候,服務端就會關閒與客戶通信的socket,通信結束。第二次客戶只輸入close,服務器響應後馬上關閉服務器,同時也關閉客戶端。下面是服務器的顯示內容:
linyongting@linyongting:~/program/c$ ./echoServer
socket create successfully
bind successfully
listen successfully
//第一次通信
number of receive bytes = 53
hello! This is my first packet.Can you reply to me?
number of receive bytes = 16
Ohh, U did it!
number of receive bytes = 20
see U next time!!!
number of receive bytes = 6
quit
//第二次通信
number of receive bytes = 7
close
server is closing
當客戶端輸入quit時,只是客戶端關閉,服務器還接著為其它客服端服務。當客戶端輸入 close時,服務關閉。
當前出現的問題:
我們的服務器序程只能與一個客戶端進行通信,只能當客戶端發出quit命令關閉後才能與下一個客戶端通信。