socket網絡通信
打算要開啟apue這一個系列的專欄,即寫即更!!
第一講:socket網絡通訊原理
文章目錄
- socket網絡通信
- 前言
-
- 一、檔案描述符是什麼?
- 二、網絡socket通訊的流程
- 1.伺服器端的邏輯流程
- 2.用戶端的邏輯流程
- 3.完整代碼如下
- 總結
前言
提示:本文講述socket網絡通信的部分,其中包括的内容有:網絡socket通信的基本流程、涉及到的主要函數等等。
在本文内容學習之前需要了解的如下
一、檔案描述符是什麼?
檔案描述符(file descriptor)是核心為了高效管理已被打開的檔案所建立的索引,用于指代被打開的檔案,對檔案所有I/O操作相關的系統調用都需要通過檔案描述符。(如果前面的太書面不好了解就看這一句:檔案辨別符是唯一辨別一個檔案,友善對檔案進行操作的一個變量)
二、網絡socket通訊的流程

1.伺服器端的邏輯流程
Socket -> bind -> listen -> accept -> 進行正常的讀寫操作 -> close
“套接字API最初是作為UNIX作業系統的一部分而開發的,是以套接字API與系統的其它I/O裝置內建在一起。因為程式要為網際網路通信而建立一個套接字(socket)時,作業系統就傳回一個小整數作為描述符(descriptor)來表示這個套接字。然後應用程式以該描述符作為傳遞參數,通過調用相應函數(如read、write、close等)來完成某種操作(如從套接字中讀取或寫入資料)”,這就類似于檔案描述符的使用,隻是使用的對象不一樣。
1.是建立socket函數,對應于普通檔案的打開操作,用于建立一個socket描述字,唯一辨別一個socket。
2.當我們調用socket()函數建立一個socket辨別符時,傳回的socket描述字它存在于協定族(address family AF_XXX)空間中,但是沒有一個具體的位址,但沒有一個具體的位址,如果想要給它指派一個位址,就必須調用bind()函數,為socket綁定一個位址。
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("create socket failure: %s\n",strerror(errno));
return -2;
}
- socket()函數建立的socket預設是一個主動類型的,but作為一個伺服器,在調用socket()、bind()函數之後會調用listen()函數來監聽這個socket,使該函數變為被動類型的,等待用戶端的連接配接請求。
4.Tcp在調用前面三個函數以後,就會監聽指定了socket位址了。伺服器就會調用accept()來接受來自用戶端的請求,這個函數預設是一個阻塞函數,當有用戶端來連接配接的時候會調用connect()函數,沒有的話就會一直阻塞。
讀寫的操作在此略過
最後和檔案的讀寫一樣,有建立打開的操作就有關閉的操作,伺服器作為服務端一般是不關閉的,用戶端于伺服器端的資訊“交流”完畢之後,關閉的是用戶端。
2.用戶端的邏輯流程
Socket -> bind -> listen -> accept -> 一般的讀寫操作 -> close
在此隻談論伺服器端沒出現的connect()函數
Tcp用戶端調用socket()建立屬于socket的辨別符之後,就可以通過connect()函數來連接配接伺服器。用戶端使用這個函數表示發出連接配接請求,伺服器端會通過accept()函數來接受請求。
ps:一般在用戶端不使用bind()函數,而是在connect()時由系統随機生成一個,但是也可以bind一個位址和端口,即使用特定的IP和端口來連伺服器。(伺服器端需要bind是因為要有一個确切的位址和端口來提供服務,用戶端就不需要)
3.完整代碼如下
socket_server.c
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LISTEN_PORT 8889
#define BACKLOG 13
int main()
{
int rv = -1;
int listen_fd, client_fd = -1;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
socklen_t cliaddr_len;
char buf[1024];
listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0)
{
printf("create socket failure: %s\n",strerror(errno));
return -1;
}
printf("socket create fd[%d]\n",listen_fd);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("create socket failure: %s\n",strerror(errno));
return -2;
}
printf("socket[%d] bind on port[%d] for all IP address ok\n",listen_fd,LISTEN_PORT);
listen(listen_fd,BACKLOG);
while(1)
{
printf("\n start waiting and accept new client connect...\n");
client_fd = accept(listen_fd,(struct sockaddr*)&cli_addr,&cliaddr_len);
if(client_fd < 0)
{
printf("accept new socket failure:%s\n",strerror(errno));
return -2;
}
printf("accept new client[%s:%d] with fd [%d]\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),client_fd);
memset(buf,0,sizeof(buf));
if((rv = read(client_fd,buf,sizeof(buf))) < 0)
{
printf("read data from client socket[%d] failure: %s\n",client_fd,strerror(errno));
close(client_fd);
continue;
}
else if(rv == 0)
{
printf("client socket[%d] disconnnect\n",client_fd);
close(client_fd);
continue;
}
printf("read %d bytes data from client[%d] and echo it back: '%s'\n",rv,client_fd,buf);
if(write(client_fd,buf,rv) < 0 )
{
printf("write %d bytes data back to client[%d] failure: %s\n",rv,client_fd,strerror(errno));
close(client_fd);
}
sleep(1);
close(client_fd);
}
close(client_fd);
}
socket_client.c
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8889
#define MSG_STR "Hello, unix network program world!!"
int main()
{
int conn_fd = -1;
int rv = -1;
char buf[1024];
struct sockaddr_in serv_addr;
conn_fd = socket(AF_INET,SOCK_STREAM,0);
if(conn_fd < 0)
{
printf("create socket failure%s\n",strerror(errno));
return -1;
}
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
inet_aton(SERVER_IP,&serv_addr.sin_addr);
if(connect(conn_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("connect to server [%s:%d] failure: %s\n",SERVER_IP,SERVER_PORT,strerror(errno));
return 0;
}
if(write(conn_fd,MSG_STR,strlen(MSG_STR)) < 0)
{
printf("write data to server [%s:%d] failure: %s\n",SERVER_IP,SERVER_PORT,strerror(errno));
goto cleanup;
}
memset(buf,0,sizeof(buf));
rv = read(conn_fd,buf,sizeof(buf));
if(rv < 0)
{
printf("read data from server failure: %s\n",strerror(errno));
goto cleanup;
}
else if(0 == rv)
{
printf("client connect to server get disconnected\n");
goto cleanup;
}
printf("read %d bytes data from server:'%s'\n",rv,buf);
cleanup:
close(conn_fd);
}
總結
這就是網絡socket通訊的流程,歡迎來信讨論,如有不對,感謝提醒。