簡單TCP通信實驗
目錄
簡單TCP通信實驗
分析
1、套接字類型
2、socket編程步驟
3、socket編程實現(xiàn)具體思路
實驗結(jié)果截圖
程序代碼
實驗設(shè)備:???
目標(biāo)系統(tǒng):windows
軟件工具:vs2022/VC6/dev
實驗要求:
- 完成TCP服務(wù)端和客戶端的程序編寫;
- 實現(xiàn)簡單字符串的收發(fā)功能。
- 需附上代碼及運行結(jié)果截圖。
實驗內(nèi)容:
分析:
1、套接字類型
? ① 流式套接字(SOCK_STREAM): 提供面向連接的、可靠的字節(jié)流服務(wù),用于TCP。
? ② 數(shù)據(jù)報套接字(SOCK_DGRAM): 提供無連接的,不可靠的數(shù)據(jù)報服務(wù),用于UDP。
? ③ 原始套接字(SOCK_RAW): 允許對較低層的協(xié)議,如IP、ICMP直接訪問。
復(fù)習(xí)TCP三次握手過程,理解TCP socket編程。
2、socket編程步驟
服務(wù)器端:
創(chuàng)建socket----socket()
綁定的socket和端口號------bind()
監(jiān)聽該端口號----listen()
接收來自客戶端的連接請求---accept()
從socket中讀取字符----recv()
關(guān)閉socket---close()
客戶端:
創(chuàng)建socket-----socket()
連接指定計算機的端口-----connect()
向socket中寫入信息-----send()
關(guān)閉socket-----close()
3、socket編程實現(xiàn)具體思路
服務(wù)器端:
其過程是首先服務(wù)器方要先啟動,并根據(jù)請求提供相應(yīng)服務(wù)
??? 1)打開一通信通道并告知本地主機,它愿意在某一公認(rèn)地址上的某端口接收客戶請求;
??? 2)等待客戶請求到達(dá)該端口;
??? 3)接收到客戶端的服務(wù)請求時,處理該請求并發(fā)送應(yīng)答信號。接收到并發(fā)服務(wù)請求,要激活一新進(jìn)程來處理這個客戶請求。新進(jìn)程處理此客戶請求,并不需要對其它請求作出應(yīng)答。服務(wù)完成后,關(guān)閉此新進(jìn)程與客戶的通信鏈路,并終止。
??? 4)返回第(2)步,等待另一客戶請求。
??? 5)關(guān)閉服務(wù)器
客戶端:
- 打開一通信通道,并連接到服務(wù)器所在主機的特定端口;
- 向服務(wù)器發(fā)服務(wù)請求報文,等待并接收應(yīng)答;繼續(xù)提出請求…
- 請求結(jié)束后關(guān)閉通信通道并終止。
- 代碼實現(xiàn)過程分析
1)、創(chuàng)建套接字----socket()
應(yīng)用程序在使用套接字前,首先必須擁有一個套接字,系統(tǒng)調(diào)用socket()向應(yīng)用程序提供創(chuàng)建套接字的手段,其調(diào)用格式如下:
SOCKET PASCAL FAR socket(int af, int type, int protocol)
該調(diào)用要接收三個參數(shù):af、type、protocol。
Af--------指定通信發(fā)生的區(qū)域:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中僅支持AF_INET,它是網(wǎng)際網(wǎng)區(qū)域。因此,地址族與協(xié)議族相同。
Type----- 描述要建立的套接字的類型。這里分三種:
一、是TCP流式套接字(SOCK_STREAM)提供了一個面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)無差錯、無重復(fù)地發(fā)送,且按發(fā)送順序接收。內(nèi)設(shè)流量控制,避免數(shù)據(jù)流超限;數(shù)據(jù)被看作是字節(jié)流,無長度限制。文件傳送協(xié)議(FTP)即使用流式套接字。
二、是數(shù)據(jù)報式套接字(SOCK_DGRAM)提供了一個無連接服務(wù)。數(shù)據(jù)包以獨立包形式被發(fā)送,不提供無錯保證,數(shù)據(jù)可能丟失或重復(fù),并且接收順序混亂。網(wǎng)絡(luò)文件系統(tǒng)(NFS)使用數(shù)據(jù)報式套接字。
三、是原始式套接字(SOCK_RAW)該接口允許對較低層協(xié)議,如IP、ICMP直接訪問。常用于檢驗新的協(xié)議實現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新設(shè)備。
Protocol-----說明該套接字使用的特定協(xié)議,如果調(diào)用者不希望特別指定使用的協(xié)議,則置為0,使用默認(rèn)的連接模式。
2)指定本地地址---bind()
當(dāng)一個套接字用socket()創(chuàng)建后,存在一個名字空間(地址族),但它沒有被命名。bind()將套接字地址(包括本地主機地址和本地端口地址)與所創(chuàng)建的套接字號聯(lián)系起來,即將名字賦予套接字,以指定本地半相關(guān)。其調(diào)用格式如下:
int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen);?
s----是由socket()調(diào)用返回的并且未作連接的套接字描述符(套接字號)。
name-----是賦給套接字s的本地地址(名字),其長度可變,結(jié)構(gòu)隨通信域的不同而不同。
namelen-----表明了name的長度。如果沒有錯誤發(fā)生,bind()返回0。否則返回SOCKET_ERROR。
3)建立套接字連接---connect()與accept()
這兩個系統(tǒng)調(diào)用用于完成一個完整相關(guān)的建立,其中connect()用于建立連接。accept()用于使服務(wù)器等待來自某客戶進(jìn)程的實際連接。connect()的調(diào)用格式如下:
int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);
參數(shù)s是欲建立連接的本地套接字描述符。
參數(shù)name指出說明對方套接字地址結(jié)構(gòu)的指針。
對方套接字地址長度由namelen說明。
如果沒有錯誤發(fā)生,connect()返回0。否則返回值SOCKET_ERROR。在面向連接的協(xié)議中,該調(diào)用導(dǎo)致本地系統(tǒng)和外部系統(tǒng)之間連接實際建立。、
accept()的調(diào)用格式如下:
SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);?
參數(shù)s為本地套接字描述符,在用做accept()調(diào)用的參數(shù)前應(yīng)該先調(diào)用過listen()。
addr指向客戶方套接字地址結(jié)構(gòu)的指針,用來接收連接實體的地址。addr的確切格式由套接字創(chuàng)建時建立的地址族決定。
addrlen為客戶方套接字地址的長度(字節(jié)數(shù))。
如果沒有錯誤發(fā)生,accept()返回一個SOCKET類型的值,表示接收到的套接字的描述符。否則返回值INVALID_SOCKET。
4)監(jiān)聽連接---listen()
此調(diào)用用于面向連接服務(wù)器,表明它愿意接收連接。listen()需在accept()之前調(diào)用,其調(diào)用格式如下:
int PASCAL FAR listen(SOCKET s, int backlog);
參數(shù)s標(biāo)識一個本地已建立、尚未連接的套接字號,服務(wù)器愿意從它上面接收請求。
backlog表示請求連接隊列的最大長度,用于限制排隊請求的個數(shù),目前允許的最大值為5。
如果沒有錯誤發(fā)生,listen()返回0。否則它返回SOCKET_ERROR。
listen()在執(zhí)行調(diào)用過程中可為沒有調(diào)用過bind()的套接字s完成所必須的連接,并建立長度為backlog的請求連接隊列。
5)數(shù)據(jù)傳輸---send()與recv()
當(dāng)一個連接建立以后,就可以傳輸數(shù)據(jù)了。常用的系統(tǒng)調(diào)用有send()和recv()。
send()調(diào)用用于s指定的已連接的數(shù)據(jù)報或流套接字上發(fā)送輸出數(shù)據(jù),格式如下:
int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);
參數(shù)s為已連接的本地套接字描述符。
buf 指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針,其長度由len 指定。
flags指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等。如果沒有錯誤發(fā)生,
send()返回總共發(fā)送的字節(jié)數(shù)。否則它返回SOCKET_ERROR。
recv()調(diào)用用于s指定的已連接的數(shù)據(jù)報或流套接字上接收輸入數(shù)據(jù),格式如下:
int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);
參數(shù)s 為已連接的套接字描述符。
buf指向接收輸入數(shù)據(jù)緩沖區(qū)的指針,其長度由len 指定。
flags指定傳輸控制方式,如是否接收帶外數(shù)據(jù)等。
如果沒有錯誤發(fā)生,recv()返回總共接收的字節(jié)數(shù)。如果連接被關(guān)閉,返回0。否則它返回SOCKET_ERROR。
6)關(guān)閉套接字---closesocket()
closesocket()關(guān)閉套接字s,并釋放分配給該套接字的資源;如果s涉及一個打開的TCP連接,則該連接被釋放。closesocket()的調(diào)用格式如下:
BOOL PASCAL FAR closesocket(SOCKET s);
參數(shù)s待關(guān)閉的套接字描述符。
如果沒有錯誤發(fā)生,closesocket()返回0。否則返回值SOCKET_ERROR。
注意:client_in.sin_addr.S_un.S_addr = inet_addr()
inet_addr()內(nèi)地址要查詢本機的ip?;蛘咧苯佑没丨h(huán)地址127.0.0.1
實驗結(jié)果截圖:
?
?
程序代碼:
客戶端:
#include<stdio.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
#include<windows.h>
int main()
{
? char sendBuf[1024];
? char receiveBuf[1024];
? while (1)
? {
??????? WSADATA wsadata;
??????? if (0 == WSAStartup(MAKEWORD(2, 2), &wsadata))
??????? {
????????????? printf("等待連接....\n");
?????????????
??????? }
??????? else
??????? {
??????????????????? printf("連接失??!\n");
?
??????? }
??????? SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
??????? SOCKADDR_IN client_in;
??????? client_in.sin_addr.S_un.S_addr = inet_addr("172.29.18.16");//將網(wǎng)絡(luò)地址字符串轉(zhuǎn)換成二進(jìn)制形式
??????? client_in.sin_family = AF_INET;
??????? client_in.sin_port = htons(6000);
??????? connect(clientSocket, (SOCKADDR*)&client_in, sizeof(SOCKADDR));
??????? recv(clientSocket, receiveBuf, 1024, 0);
??????? printf("收到來自服務(wù)器:? %s\n", receiveBuf);
? ??? printf("向服務(wù)器發(fā)出: ");
??????? gets(sendBuf);
??????? send(clientSocket, sendBuf, 1024, 0);
??????? closesocket(clientSocket);
??????? WSACleanup();
? }
? return 0;文章來源:http://www.zghlxwxcb.cn/news/detail-471455.html
}文章來源地址http://www.zghlxwxcb.cn/news/detail-471455.html
服務(wù)端:
#include<stdio.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
#include<windows.h>
int main()
{
? char sendBuf[1024];
? char receiveBuf[1024];
? while (1)
? {
??????? //創(chuàng)建套接字,socket前的一些檢查工作.
? //服務(wù)的啟動
??????? WSADATA wsadata;//wsa 即windows socket async 異步套接字
??????? if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata))
??????? {
????????????? printf("未連接\n");
????????????? return 0;
??????? }
??????? else
??????? {
????????????? printf("連接成功!\n");
??????? }
?????? SOCKET serSocket = socket(AF_INET, SOCK_STREAM, 0);//創(chuàng)建可識別的套接字//parm1: af 地址協(xié)議族 ipv4 ipv6
??????? ?????????????????????????????????????????????????? //parm2:type 傳輸協(xié)議類型 流式套接字(SOCK_STREAM),數(shù)據(jù)包套接字(SOCK_DGRAM)
??????? ?????????????????????????????????????????????????? //parm3:ptotoc1 使用具體的某個傳輸協(xié)議
???????
??????? SOCKADDR_IN addr;????????????????????????????????? //需要綁定的參數(shù),主要是本地的socket的一些信息。
??????? addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);???? //ip地址,htonl即host本機 to:to? n:net l:unsigned long 大端存儲,低字節(jié)在高位
??????? addr.sin_family = AF_INET;
??????? addr.sin_port = htons(6000);?????????????????????? //端口 htons將無符號短整型轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序
??????? bind(serSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR));
??????? listen(serSocket, 5);?????????????????????????????
??????? SOCKADDR_IN clientsocket;
??????? int len = sizeof(SOCKADDR);
??????? SOCKET serConn = accept(serSocket, (SOCKADDR*)&clientsocket, &len);
??????? printf("向客戶端發(fā)出: \n");
??????? gets(sendBuf);
??????? send(serConn, sendBuf, 1024, 0);
??????? recv(serConn, receiveBuf,1024, 0);
?
??????? printf("收到來自客戶端: %s\n", receiveBuf);
??????? closesocket(serConn);//關(guān)閉
??????? WSACleanup();//釋放資源
? }
? return 0;
}
到了這里,關(guān)于服務(wù)端和客戶端通信-TCP(含完整源代碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!