歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux服務器

Linux socket編程實例:echo服務器程序

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命令關閉後才能與下一個客戶端通信。

Copyright © Linux教程網 All Rights Reserved