本文給出基於ipv4的tcp客戶端以及服務端程式樣例。
服務端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define backlog 16
int listen_fd = -1;
void sigint(int signo);
int main()
// socket
if ( (listen_fd = socket(af_inet, sock_stream, 0)) < 0 )
// bind
struct sockaddr_in server_addr;
server_addr.sin_family = af_inet; // ipv4
server_addr.sin_port = htons(12500); // port
server_addr.sin_addr.s_addr = htonl(inaddr_any); // ip
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
// listen
if (listen(listen_fd, backlog) < 0)
printf("server init ok, start to accept new connect...\n");
while (1)
printf("accept one new connect(%d)!!!\n", client_fd);
static
const
char *msg = "hello, client!\n";
if (write(client_fd, msg, strlen(msg)) != strlen(msg))
close(client_fd);
}}void sigint(int signo)
客戶端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
struct sockaddr_in server_addr;
server_addr.sin_family = af_inet;
server_addr.sin_port = htons(12500);
if (inet_pton(af_inet, "127.0.0.1", &server_addr.sin_addr) <= 0)
if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
printf("connect to server ok!\n");
char msg[1024];
int rbytes = -1;
while ( (rbytes = read(client_fd, msg, sizeof(msg)-1)) > 0)
if (rbytes < 0)
exit(0);
}
服務端注意事項:
其中inaddr_any是用來設定「協議位址」中ip部分。
例如服務端所在的主機有三個ip:
10.1.1.1
10.1.1.2
10.1.1.3
如果設定「協議位址」的ip部分為inaddr_any(port=12500),則:
1)有客戶端鏈結10.1.1.1:12500,服務端可以收到syn包並建立鏈結;
2)有客戶端鏈結10.1.1.2:12500,服務端可以收到syn包並建立鏈結;
3)有客戶端鏈結10.1.1.3:12500,服務端可以收到syn包並建立鏈結;
如果設定「協議位址」的ip部分為10.1.1.3(port=12500),則:
1)有客戶端鏈結10.1.1.1:12500,服務端不可以收到syn包並建立鏈結;
2)有客戶端鏈結10.1.1.2:12500,服務端不可以收到syn包並建立鏈結;
3)有客戶端鏈結10.1.1.3:12500,服務端可以收到syn包並建立鏈結;
執行服務端程式並且執行三次客戶端程式,可以發現服務端有以下輸出:
[jiang@localhost
0406]$ ./server
server
init
ok, start
toaccept
newconnection...
accept
onenew
connect(4)!!!
accept
onenew
connect(4)!!!
accept
onenew
connect(4)!!!
^ccatch
sigint, quit...
為啥每次accept返回的客戶端的sockfd都相同呢?
首先服務端啟動時,fd已經有三個(0=標準輸入;1=標準輸出;2=標準錯誤),然後通過socket介面建立了listen_fd(3)。
然後通過accept,從已完成三次握手的佇列中取出乙個新的客戶端鏈結,建立乙個fd,此fd即為4。
服務端每次的行為都一樣,先accept生成fd(4),然後寫入資料到fd=4中,然後close(fd=4)。
在close後,當前服務端程序中的fd只剩下4個(0、1、2、3),fd=4又變為可用項。
當服務端再次accept生成新的fd時,核心總是從最低可用的fd開始建立占用,由於當前服務端程序有四個fd有各自用途,自然從fd=4重新開始。
每次都是close(fd=4),釋放fd=4的狀態為可用fd,然後accept時建立fd=4,指代新的客戶端socket,往復迴圈。
客戶端注意事項:
在客戶端connect鏈結服務端成功後,開始從中read資料,直到read不大於0。
read返回值:
1)>0:讀到返回值大小的位元組資料;
2)=0:對端關閉連線;
3)<0:read出現錯誤;
由於tcp是一種「資料流」,服務端寫資料,客戶端讀資料,那客戶端怎麼知道啥時候「服務端要傳送的全傳送完畢啦!」這個資訊呢?
我們在這裡約定,當服務端將要傳送的資料全部傳送完畢時,就close客戶端,通知客戶端:我(服務端)要傳送給你(客戶端)的資料已經傳送完畢啦!客戶端read到0,得到了這個資訊,於是乎就不再(能)繼續讀。當然,可以由客戶端不斷從收到的資料中檢查某些約定好的「協議控制字元(組)」(這裡的協議控制字元可以是任何字元,並非傳統意義上的控制字元),例如,約定讀取到兩個\n就算做乙個記錄已經被讀取完畢,開始下乙個記錄…即:人為約定、界定一條有效資料(或稱為一條記錄)的開始和結束。
更何況,萬一在read時有訊號來了,打斷了read呼叫,導致只從核心接收緩衝區中讀取了部分位元組到使用者態,僅僅呼叫一次read函式,不管緩衝區中是否有剩餘資料,豈不是會導致資料丟失?
其實到這裡已經有點偏離主題了…本意是給出乙份客戶端、服務端關於socket api的使用樣例,但是…
我上面的程式距離真正可用、能用、實用還有很多路要走,存在有很多可優化的地方,比如「so_reuseaddr 」等…
python tcp網路程式 客戶端 服務端
所謂的伺服器端 就是提供服務的一方,而客戶端,就是需要被服務的一方 比如乙個人想打10086求助人工服務。tcp的客戶端要比伺服器端簡單很多,客戶端只需要找乙個 亭,拿起 撥打即可,流程要少很多 示例 from socket import 建立socket tcp client socket soc...
Linux UDP服務端和客戶端程式
udp服務端 brief udp服務端 author mculover666 date 2020 04 15 include include include include include include include include intmain int argc,char ar printf...
Python UDP客戶端 服務端
udpclient.py coding utf 8 from socket import servername 127.0.0.1 伺服器位址,本例中使用一台遠端主機 serverport 12000 伺服器指定的埠 clientsocket socket af inet,sock dgram 建立...