Socket编程(C语言实现)——TCP协议(网络间通信AF_INET)的流式(SOCK_STREAM)+报式(SOCK_DGRAM)传输【多线程+循环监听】
Socket編程
目前較為流行的網絡編程模型是客戶機/服務器通信模式
客戶進程向服務器進程發出要求某種服務的請求,服務器進程響應該請求。如圖所示,通常,一個服務器進程會同時為多個客戶端進程服務,圖中服務器進程B1同時為客戶進程A1、A2和B2提供服務。
Socket概述
①?? 所謂Socket通常也稱作“套接字”,用于描述IP地址和端口,是一個通信鏈的句柄。應用程序通常通過“套接字”向網絡發出請求或者應答網絡請求。
②?? Socket是連接運行在網絡上的兩個程序間的雙向通信的端點。
③?? 網絡通訊其實指的就是Socket間的通訊。
④?? 通訊的兩端都有Socket,數據在兩個Socket之間通過IO來進行傳輸。
套接字socket的類型
(1)流式套接字(SOCK_STREAM)
提供面向連接、可靠的數據傳輸服務,數據無差錯、無重復的發送,且按發送順序接收(TCP協議)
(2)數據報式套接字(SOCK_DGRAM)
提供無連接服務,數據包以獨立包形式發送,不提供無措保證,數據可能丟失,并且接收順序混亂(UDP協議)
(3)原始套接字(SOCK_RAM)
套接字(socket)
socket起源于Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。Socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉).說白了Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
隨著Unix的應用推廣,套接字有被引進了windows等操作系統。套接字通常只與同一區域的套接字交換數據,windows socket只支持一個通信區域:網際域(AF_INET),這個域被使用忘記協議簇的通信進程使用。
使用Socket進行網絡通信的過程
①?? 服務器程序將一個套接字綁定到一個特定的端口,并通過此套接字等待和監聽客戶的連接請求。
②?? 客戶程序根據服務器程序所在的主機和端口號發出連接請求。
③?? 如果一切正常,服務器接受連接請求。并獲得一個新的綁定到不同端口地址的套接字。
④?? 客戶和服務器通過讀、寫套接字進行通訊。
客戶機/服務器模式
在TCP/IP網絡應用中,通信的兩個進程間相互作用的主要模式是客戶機/服務器模式*(client/server),即客戶像服務其提出請求,服務器接受到請求后,提供相應的服務。
服務器:
(1)首先服務器方要先啟動,打開一個通信通道并告知本機,它愿意在某一地址和端口上接收客戶請求
(2)等待客戶請求到達該端口
(3)接收服務請求,處理該客戶請求,服務完成后,關閉此新進程與客戶的通信鏈路,并終止
(4)返回第二步,等待另一個客戶請求
(5)關閉服務器
客戶方:
(1)打開一個通信通道,并連接到服務器所在的主機特定的端口
(2)向服務器發送請求,等待并接收應答,繼續提出請求
(3)請求結束后關閉通信信道并終止
基于TCP(面向連接)的socket編程
?流式傳輸:“客戶端”,1.socket()函數;2.bind()函數可有可無,加上指定傳輸端口,不加隨機分配端口;3.connect()函數,填寫服務端的地址與端口【網絡間通信AF_STREAM】或者路徑【進程間通信AF_DGRAM】;4.send()函數;5.recv()函數。
流式傳輸:“服務端”,1.socket()函數;2.bind()函數,必須加上指定傳輸端口【網絡間通信AF_STREAM】或者是路徑【進程間通信AF_DGRAM】 ;3.listen()函數,使用isockfd;5.accepc()函數,生成新的fd,inewfd;6.send()函數,inewfd;7.recv()函數,inewfd。
服務器端先初始化Socket,然后與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然后連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求并處理請求,然后把回應數據發送給客戶端,客戶端讀取數據,最后關閉連接,一次交互結束。
基于TCP(面向連接)的socket編程——流式套接字(SOCK_STREAM)
網絡間通信AF_INET,典型的TCP/IP四型模型的通信過程
服務器:
#include <stdio.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h>#define PORT 23 //端口號 #define BACKLOG 5 //最大監聽數int main() {int iSocketFD = 0; //socket句柄int iRecvLen = 0; //接收成功后的返回值int new_fd = 0; //建立連接后的句柄char buf[4096] = {0}; //struct sockaddr_in stLocalAddr = {0}; //本地地址信息結構圖,下面有具體的屬性賦值struct sockaddr_in stRemoteAddr = {0}; //對方地址信息socklen_t socklen = 0; iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socketif(0 > iSocketFD){printf("創建socket失敗!\n");return 0;} stLocalAddr.sin_family = AF_INET; /*該屬性表示接收本機或其他機器傳輸*/stLocalAddr.sin_port = htons(PORT); /*端口號*/stLocalAddr.sin_addr.s_addr=htonl(INADDR_ANY); /*IP,括號內容表示本機IP*///綁定地址結構體和socketif(0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr))){printf("綁定失敗!\n");return 0;}//開啟監聽 ,第二個參數是最大監聽數if(0 > listen(iSocketFD, BACKLOG)){printf("監聽失敗!\n");return 0;}printf("iSocketFD: %d\n", iSocketFD); //在這里阻塞知道接收到消息,參數分別是socket句柄,接收到的地址信息以及大小?new_fd = accept(iSocketFD, (void *)&stRemoteAddr, &socklen);if(0 > new_fd){printf("接收失敗!\n");return 0;}else{printf("接收成功!\n");//發送內容,參數分別是連接句柄,內容,大小,其他信息(設為0即可)?send(new_fd, "這是服務器接收成功后發回的信息!", sizeof("這是服務器接收成功后發回的信息!"), 0);}printf("new_fd: %d\n", new_fd); iRecvLen = recv(new_fd, buf, sizeof(buf), 0); if(0 >= iRecvLen) //對端關閉連接 返回0{ printf("接收失敗或者對端關閉連接!\n");}else{printf("buf: %s\n", buf);}close(new_fd);close(iSocketFD);return 0; }客戶端:
#include <stdio.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h>#define PORT 23 //目標地址端口號 #define ADDR "192.168.1.230" //目標地址IPint main() {int iSocketFD = 0; //socket句柄unsigned int iRemoteAddr = 0;struct sockaddr_in stRemoteAddr = {0}; //對端,即目標地址信息socklen_t socklen = 0; char buf[4096] = {0}; //存儲接收到的數據iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socketif(0 > iSocketFD){printf("創建socket失敗!\n");return 0;} stRemoteAddr.sin_family = AF_INET;stRemoteAddr.sin_port = htons(PORT);inet_pton(AF_INET, ADDR, &iRemoteAddr);stRemoteAddr.sin_addr.s_addr=iRemoteAddr;//連接方法: 傳入句柄,目標地址,和大小if(0 > connect(iSocketFD, (void *)&stRemoteAddr, sizeof(stRemoteAddr))){printf("連接失敗!\n");//printf("connect failed:%d",errno);//失敗時也可打印errno}else{printf("連接成功!\n");recv(iSocketFD, buf, sizeof(buf), 0); 將接收數據打入buf,參數分別是句柄,儲存處,最大長度,其他信息(設為0即可)。?printf("Received:%s\n", buf);}close(iSocketFD);//關閉socket return 0; }測試:
1、編譯服務器、客戶端代碼:
[root@localhost tcp_socket]# make socket_server_tcp cc socket_server_tcp.c -o socket_server_tcp [root@localhost tcp_socket]# make socket_client_tcp cc socket_client_tcp.c -o socket_client_tcp [root@localhost tcp_socket]#2、服務器端口監聽:
[root@localhost tcp_socket]# ./socket_server_tcp3、執行客戶端:
非telnet:
服務器端顯示: [root@localhost tcp_socket]# ./socket_server_tcp iSocketFD: 3 接收成功! new_fd: 4 接收失敗或者對端關閉連接!客戶端顯示: [root@localhost tcp_socket]# ./socket_client_tcp 連接成功! Received:這是服務器接收成功后發回的信息!telnet服務器:
服務器端顯示: [root@localhost tcp_socket]# ./socket_server_tcp iSocketFD: 3 接收成功! new_fd: 4 buf: �������� ��!��"��'����#客戶端顯示: [root@localhost tcp_socket]# telnet 192.168.1.230 Trying 192.168.1.230... Connected to 192.168.1.230. Escape character is '^]'. 這是服務器接收成功后發回的信息!Connection closed by foreign host.?
基于TCP(面向連接)的socket編程(多線程、循環監聽)——數據報式套接字(SOCK_DGRAM)
網絡間通信AF_INET,典型的TCP/IP四型模型的通信過程
服務器:(多線程的【每10秒會打印一行#號】? ?與 ? 循環監聽)
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <pthread.h>void * test(void *pvData) {while(1){sleep(10);printf("################################\n");}return NULL; }int main(void) {pthread_t stPid = 0; int iRecvLen = 0;int iSocketFD = 0;char acBuf[4096] = {0};struct sockaddr_in stLocalAddr = {0};struct sockaddr_in stRemoteAddr = {0};socklen_t iRemoteAddrLen = 0;/* 創建socket */iSocketFD = socket(AF_INET, SOCK_DGRAM, 0);if(iSocketFD < 0){printf("創建socket失敗!\n");return 0;}/* 填寫地址 */stLocalAddr.sin_family = AF_INET;stLocalAddr.sin_port = htons(12345);stLocalAddr.sin_addr.s_addr = 0;/* 綁定地址 */if(0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr))){printf("綁定地址失敗!\n");close(iSocketFD);return 0;}pthread_create(&stPid, NULL, test, NULL); //實現了多線程while(1) //實現了循環監聽{iRecvLen = recvfrom(iSocketFD, acBuf, sizeof(acBuf), 0, (void *)&stRemoteAddr, &iRemoteAddrLen);printf("iRecvLen: %d\n", iRecvLen);printf("acBuf:%s\n", acBuf);}close(iSocketFD);return 0; }客戶端:
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h>int main(void) {int iRecvLen = 0;int iSocketFD = 0;int iRemotAddr = 0;char acBuf[4096] = {0};struct sockaddr_in stLocalAddr = {0};struct sockaddr_in stRemoteAddr = {0};socklen_t iRemoteAddrLen = 0;/* 創建socket */iSocketFD = socket(AF_INET, SOCK_DGRAM, 0);if(iSocketFD < 0){printf("創建socket失敗!\n");return 0;}/* 填寫服務端地址 */stLocalAddr.sin_family = AF_INET;stLocalAddr.sin_port = htons(12345);inet_pton(AF_INET, "192.168.1.230", (void *)&iRemotAddr);stLocalAddr.sin_addr.s_addr = iRemotAddr;iRecvLen = sendto(iSocketFD, "這是一個測試字符串", strlen("這是一個測試字符串"), 0, (void *)&stLocalAddr, sizeof(stLocalAddr));close(iSocketFD);return 0; }測試:
1、編譯服務器:因為有多線程,所以服務器端進程要進行pthread編譯:
[root@localhost udp_socket]# gcc socket_server_UDP.c -pthread -g -o socket_server_UDP2、服務器監聽:
[root@localhost udp_socket]# ./socket_server_UDP3、客戶端連接服務器:
[root@localhost tcp_socket]# ./socket_client_UDP4、服務器端口顯示結果:
[root@localhost udp_socket]# ./socket_server_UDP iSocketFD: 3 ################################ ################################ iRecvLen: 27 acBuf:這是一個測試字符串 iSocketFD: 3 ################################5、結果解釋說明:
服務器端有主線程和輔線程,主線程,打印客戶端發送的請求;輔線程每隔10秒鐘打印一排#號。
?
參考鏈接:https://blog.csdn.net/zhang___yong/article/details/78702559
java:https://www.cnblogs.com/wzy330782/p/5479833.html
基于TCP/IP和UDP協議的socket編程結構解析:https://blog.csdn.net/zhengnice/article/details/51428080
總結
以上是生活随笔為你收集整理的Socket编程(C语言实现)——TCP协议(网络间通信AF_INET)的流式(SOCK_STREAM)+报式(SOCK_DGRAM)传输【多线程+循环监听】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux查找文件 —— wherei
- 下一篇: Socket编程(C语言实现)—— Ng