socket网络编程——TCP编程流程及端口号占用问题
1.TCP編程流程
1.1TCP服務器端客戶端及方法介紹
TCP 提供的是面向連接的、可靠的、字節流服務。TCP 的服務器端和客戶端編程流程如下:
socket()方法是用來創建一個套接字,有了套接字就可以通過網絡進行數據的收發。這也是為什么進行網絡通信的程序首先要創建一個套接字。創建套接字時要指定使用的服務類型,使用 TCP 協議選擇流式服務(SOCK_STREAM)。
bind()方法是用來指定套接字使用的 IP 地址和端口。IP 地址就是自己主機的地址,如果主機沒有接入網絡,測試程序時可以使用回環地址“127.0.0.1”。端口是一個 16 位的整形值,一般 0-1024 為知名端口,如 HTTP 使用的 80 號端口。這類端口一般用戶不能隨便使用。其次,1024-4096 為保留端口,用戶一般也不使用。4096 以上為臨時端口,用戶可以使用。在Linux 上,1024 以內的端口號,只有 root 用戶可以使用。
listen()方法是用來創建監聽隊列。監聽隊列有兩種,一個是存放未完成三次握手的連接,一種是存放已完成三次握手的連接。listen()第二個參數就是指定已完成三次握手隊列的長度。
accept()方法處理存放在 listen 創建的已完成三次握手的隊列中的連接。每處理一個連接,則accept()返回該連接對應的套接字描述符。如果該隊列為空,則 accept 阻塞。
connect()方法一般由客戶端程序執行,需要指定連接的服務器端的 IP 地址和端口。該方法執行后,會進行三次握手,建立連接。
send()方法用來向 TCP 連接的對端發送數據。send()執行成功,只能說明將數據成功寫入到發送端的發送緩沖區中,并不能說明數據已經發送到了對端。send()的返回值為實際寫入
到發送緩沖區中的數據長度。
recv()方法用來接收 TCP 連接的對端發送來的數據。recv()從本端的接收緩沖區中讀取數據,如果接收緩沖區中沒有數據,則 recv()方法會阻塞。返回值是實際讀到的字節數,如果
recv()返回值為 0, 說明對方已經關閉了 TCP 連接。
close()方法用來關閉 TCP 連接。此時,會進行四次揮手。
1.2服務端客服端示例代碼
TCP服務端代碼示例(方法參數意思參考套接字地址結構和網絡編程接口):
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<assert.h> #include<sys/socket.h> #include<arpa/inet.h>int main() {int sockfd = socket(AF_INET,SOCK_STREAM,0);assert(sockfd != -1);struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);//將短整形主機字節轉換為網絡字節saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//回環地址int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res != -1);res = listen(sockfd,5);assert(res != -1);while(1)//服務器循環接收客戶端連接{struct sockaddr_in caddr;int len = sizeof(caddr);int c = accept(sockfd,(struct sockaddr*)&caddr,&len);if(c == -1){perror("accept error");continue;}printf("accept c = %d\n",c);char data[128];int n = recv(c,data,127,0);//阻塞printf("n = %d,buff = %s\n",n,data);send(c,"OK",2,0);close(c);}close(sockfd);exit(0);}TCP客戶端代碼示例:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<assert.h> #include<sys/socket.h> #include<arpa/inet.h>int main() {int sockfd = socket(AF_INET,SOCK_STREAM,0);assert(sockfd != -1);struct sockaddr_in saddr;saddr.sin_port = htons(6000);saddr.sin_family = AF_INET;saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res != -1);printf("please input:");fflush(stdout);char buff[128] = {0};fgets(buff,127,stdin);send(sockfd,buff,strlen(buff),0);char data[128] = {0};int n = recv(sockfd,data,127,0);printf("%s\n",data);close(sockfd);exit(0);}運行結果(服務端):
客戶端:
客戶端循環發送示例代碼 :
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<assert.h> #include<sys/socket.h> #include<arpa/inet.h>int main() {int sockfd = socket(AF_INET,SOCK_STREAM,0);assert(sockfd != -1);struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res != -1);while(1){char buff[128] = {0};printf("input:\n");fgets(buff,127,stdin);if(strncmp(buff,"end",3) == 0){break;}send(sockfd,buff,strlen(buff),0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("buff = %s\n",buff);}close(sockfd); }2.端口號占用問題
當我們執行完服務端客戶端代碼后,再次執行客戶端的時候發現執行不起來:
這個assert斷言是下圖這個地方出現問題。
這是怎么回事呢?
這是因為我們對上次服務端執行后,端口還沒來得及釋放,6000這個端口仍然被占用著。我們可以用命令netstat -anp | grep +端口號或者netstat -tunlp | grep + 端口號查看這個端口被哪個進程占用著。注意:LISTEN才表示正在被占用
也可以使用netstat -nultp命令查看當前所有已經使用的端口的情況。
總結
以上是生活随笔為你收集整理的socket网络编程——TCP编程流程及端口号占用问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: socket网络编程——网络编程接口
- 下一篇: socket网络编程——多进程、多线程处