Unix/Linux Socket 服務器代碼實例

我們已經看過關於服務器socket創建的教程,爲了使進程TCP服務器端需要執行以下步驟:

  1. 創建一個socket使用socket() 系統調用.

  2. 使用*bind()*系統調用套接字綁定到一個地址。對於互聯網上的服務器套接字,地址包括主機的端口號。 

  3. 使用*listen()*系統調用連接監聽。

  4. accept() 系統調用形式接受連接。此調用通常會阻塞,直到有客戶端與服務器連接。

  5. 發送和接收數據,使用read() 和 write() 系統調用。

現在要把上面這些步驟的形式寫成源代碼。把這個代碼寫server.c文件並用gcc編譯器編譯。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main( int argc, char *argv[] )
{
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;

/\* First call to socket() function \*/
sockfd = socket(AF\_INET, SOCK\_STREAM, 0);
if (sockfd < 0) 
{
    perror("ERROR opening socket");
    exit(1);
}
/\* Initialize socket structure \*/
bzero((char \*) &serv\_addr, sizeof(serv\_addr));
portno = 5001;
serv\_addr.sin\_family = AF\_INET;
serv\_addr.sin\_addr.s\_addr = INADDR\_ANY;
serv\_addr.sin\_port = htons(portno);

/\* Now bind the host address using bind() call.\*/
if (bind(sockfd, (struct sockaddr \*) &serv\_addr,
                      sizeof(serv\_addr)) < 0)
{
     perror("ERROR on binding");
     exit(1);
}

/\* Now start listening for the clients, here process will
\* go in sleep mode and will wait for the incoming connection
\*/
listen(sockfd,5);
clilen = sizeof(cli\_addr);

/\* Accept actual connection from the client \*/
newsockfd = accept(sockfd, (struct sockaddr \*)&cli\_addr, 
                            &clilen);
if (newsockfd < 0) 
{
    perror("ERROR on accept");
    exit(1);
}
/\* If connection is established then start communicating \*/
bzero(buffer,256);
n = read( newsockfd,buffer,255 );
if (n < 0)
{
    perror("ERROR reading from socket");
    exit(1);
}
printf("Here is the message: %s\\n",buffer);

/\* Write a response to the client \*/
n = write(newsockfd,"I got your message",18);
if (n < 0)
{
    perror("ERROR writing to socket");
    exit(1);
}
return 0; 

}

處理多個連接:

爲了使服務器能夠同時處理多個連接,我們在上面的代碼進行以下更改:

  1. 將accept語句和下面的代碼在一個無限循環。

  2. 建立連接後,調用fork()創建一個新的進程。

  3. 子進程將關閉sockfd中致電doprocessing函數,通過新的套接字文件描述符作爲參數。當兩個進程已經完成了他們對話中表示由*doprocessing()*返回,這個過程簡單地退出。

  4. 父進程關閉newsockfd。因爲所有這些代碼是在一個無限循環,它接受語句將返回到等待下一個連接。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main( int argc, char *argv[] )
{
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;

/\* First call to socket() function \*/
sockfd = socket(AF\_INET, SOCK\_STREAM, 0);
if (sockfd < 0) 
{
    perror("ERROR opening socket");
    exit(1);
}
/\* Initialize socket structure \*/
bzero((char \*) &serv\_addr, sizeof(serv\_addr));
portno = 5001;
serv\_addr.sin\_family = AF\_INET;
serv\_addr.sin\_addr.s\_addr = INADDR\_ANY;
serv\_addr.sin\_port = htons(portno);

/\* Now bind the host address using bind() call.\*/
if (bind(sockfd, (struct sockaddr \*) &serv\_addr,
                      sizeof(serv\_addr)) < 0)
{
     perror("ERROR on binding");
     exit(1);
}
/\* Now start listening for the clients, here 
 \* process will go in sleep mode and will wait 
 \* for the incoming connection
 \*/
listen(sockfd,5);
clilen = sizeof(cli\_addr);
while (1) 
{
    newsockfd = accept(sockfd, 
            (struct sockaddr \*) &cli\_addr, &clilen);
    if (newsockfd < 0)
    {
        perror("ERROR on accept");
        exit(1);
    }
    /\* Create child process \*/
    pid = fork();
    if (pid < 0)
    {
        perror("ERROR on fork");
    exit(1);
    }
    if (pid == 0)  
    {
        /\* This is the client process \*/
        close(sockfd);
        doprocessing(newsockfd);
        exit(0);
    }
    else
    {
        close(newsockfd);
    }
} /\* end of while \*/

}

Here is the simple implementation of doprocessing function.

void doprocessing (int sock)
{
int n;
char buffer[256];

bzero(buffer,256);

n = read(sock,buffer,255);
if (n < 0)
{
    perror("ERROR reading from socket");
    exit(1);
}
printf("Here is the message: %s\\n",buffer);
n = write(sock,"I got your message",18);
if (n < 0) 
{
    perror("ERROR writing to socket");
    exit(1);
}

}