原文:
WSA函數(shù):https://blog.csdn.net/wangjiechen/article/details/52172885?ops_request_misc=&request_id=&biz_id=102&utm_term=windows%20socket%20api&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-52172885
簡單TCP程序教程:
https://www.cnblogs.com/DOMLX/p/9601511.html
WSA函數(shù)部分做了一些排版和補(bǔ)充仁烹,改善了一下可讀性。
TCP程序可以直接跑一下幫助理解TCP是怎么工作的筒占,想了解詳情請看原文妻献。
Windows Socket API
1.WSAStartup
int WSAStartup(WORD wVersionRequested(指定socket版本), LPWSADATA lpWSAData);
使用Socket的程序在使用Socket之前必須調(diào)用WSAStartup函數(shù)蛛株。
參數(shù) & 返回值
-
第一個(gè)參數(shù):
指明程序請求使用的Socket版本团赁,其中高位字節(jié)指明副版本、低位字節(jié)指明主版本谨履;
-
第二個(gè)參數(shù):
操作系統(tǒng)利用第二個(gè)參數(shù)返回請求的Socket的版本信息欢摄。
-
返回值:
若函數(shù)執(zhí)行成功則返回0。
當(dāng)一個(gè)應(yīng)用程序調(diào)用WSAStartup函數(shù)時(shí)屉符,操作系統(tǒng)根據(jù)請求的Socket版本來搜索相應(yīng)的Socket庫剧浸,然后綁定找到的Socket庫到該應(yīng)用程序中锹引。以后應(yīng)用程序就可以調(diào)用所請求的Socket庫中的其它Socket函數(shù)了矗钟。
涉及數(shù)據(jù)類型
LPWSADATA
:
指向WSADATA數(shù)據(jù)結(jié)構(gòu)體指針,接收Windows Socket的實(shí)現(xiàn)細(xì)節(jié)嫌变。
WSADATA
:
這個(gè)結(jié)構(gòu)被用來存儲(chǔ)被WSAStartup函數(shù)調(diào)用后返回的Windows Sockets數(shù)據(jù)吨艇。它包含Winsock.dll執(zhí)行的數(shù)據(jù)。
實(shí)例
實(shí)例:假如一個(gè)程序要使用2.1版本的Socket,那么程序代碼如下
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD( 2, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
2.WSACleanup
int WSACleanup (void);
應(yīng)用程序在完成對(duì)請求的Socket庫的使用后腾啥,要調(diào)用WSACleanup
函數(shù)來<u>解除與Socket庫的綁定</u>并且釋放Socket庫所占用的系統(tǒng)資源东涡。
3.socket
SOCKET socket(int af(協(xié)議組), int type(套接字類型), int protocol(指定具體協(xié)議));
用來創(chuàng)建一個(gè)能夠進(jìn)行網(wǎng)絡(luò)通信的套接字。
參數(shù) & 返回值
-
第一個(gè)參數(shù):
指定應(yīng)用程序使用的通信協(xié)議的協(xié)議族倘待,對(duì)于TCP/IP協(xié)議族疮跑,該參數(shù)置PF_INET;
-
第二個(gè)參數(shù):
指定要?jiǎng)?chuàng)建的套接字類型,流套接字類型為<u>SOCK_STREAM</u>凸舵、數(shù)據(jù)報(bào)套接字類型為<u>SOCK_DGRAM</u>祖娘;
-
第三個(gè)參數(shù):
指定應(yīng)用程序所使用的通信協(xié)議。(如果里邊就一種協(xié)議那就0)
-
返回值:
如果調(diào)用成功就返回新創(chuàng)建的套接字的描述符啊奄,如果失敗就返回INVALID_SOCKET渐苏。
涉及數(shù)據(jù)類型
SOCKET
是套接字描述符的數(shù)據(jù)類型,用來標(biāo)識(shí)一個(gè)套接字菇夸,而socket是創(chuàng)建套接字的函數(shù)琼富。
套接字描述符:
- 是一個(gè)整數(shù)類型的值。
- 每個(gè)進(jìn)程的進(jìn)程空間里都有一個(gè)套接字描述符表庄新,存放著套接字描述符和套接字?jǐn)?shù)據(jù)結(jié)構(gòu)的對(duì)應(yīng)關(guān)系鞠眉。
- 該表中有一個(gè)字段存放新創(chuàng)建的套接字的描述符,另一個(gè)字段存放套接字?jǐn)?shù)據(jù)結(jié)構(gòu)的地址择诈,因此根據(jù)套接字描述符就可以找到其對(duì)應(yīng)的套接字?jǐn)?shù)據(jù)結(jié)構(gòu)械蹋。
- 每個(gè)進(jìn)程在自己的進(jìn)程空間里都有一個(gè)套接字描述符表但是套接字?jǐn)?shù)據(jù)結(jié)構(gòu)都是在操作系統(tǒng)的內(nèi)核緩沖里。
實(shí)例
//協(xié)議組:AF_INET
//套接字類型:SOCK_STREAM吭从,即TCP傳輸
//某協(xié)議中只有一種特定類型時(shí)朝蜘,protocol參數(shù)設(shè)置為0
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
4.closesocket
int closesocket(SOCKET s);
closesocket函數(shù)用來關(guān)閉一個(gè)描述符為s套接字。
參數(shù) & 返回值
- 參數(shù):
要關(guān)閉的描述符s涩金。
- 返回值:
closesocket函數(shù)如果執(zhí)行成功就返回0谱醇,否則返回SOCKET_ERROR暇仲。
機(jī)制:
由于每個(gè)進(jìn)程中都有一個(gè)套接字描述符表,表中的每個(gè)套接字描述符都對(duì)應(yīng)了一個(gè)位于操作系統(tǒng)緩沖區(qū)中的套接字?jǐn)?shù)據(jù)結(jié)構(gòu)副渴,因此有可能有幾個(gè)套接字描述符指向同一個(gè)套接字?jǐn)?shù)據(jù)結(jié)構(gòu)奈附。
套接字?jǐn)?shù)據(jù)結(jié)構(gòu)中專門有一個(gè)字段存放該結(jié)構(gòu)的被引用次數(shù),即有多少個(gè)套接字描述符指向該結(jié)構(gòu)煮剧。
當(dāng)調(diào)用closesocket函數(shù)時(shí)斥滤,操作系統(tǒng)先檢查套接字?jǐn)?shù)據(jù)結(jié)構(gòu)中的該字段的值,如果為1勉盅,就表明只有一個(gè)套接字描述符指向它佑颇,因此操作系統(tǒng)就先把s在套接字描述符表中對(duì)應(yīng)的那條表項(xiàng)清除,并且釋放s對(duì)應(yīng)的套接字?jǐn)?shù)據(jù)結(jié)構(gòu)草娜;
如果該字段大于1挑胸,那么操作系統(tǒng)僅僅清除s在套接字描述符表中的對(duì)應(yīng)表項(xiàng),并且把s對(duì)應(yīng)的套接字?jǐn)?shù)據(jù)結(jié)構(gòu)的引用次數(shù)減1宰闰。
5.send
int send(SOCKET s(指定發(fā)送端套接字), const char FAR *buf(指定一塊緩沖區(qū)), int len(指定要發(fā)送的字節(jié)數(shù)), int flags );
使用send函數(shù)向TCP連接的另一端發(fā)送請求茬贵。
參數(shù)
-
第一個(gè)參數(shù):
指定一個(gè)發(fā)送端套接字描述符;
-
第二個(gè)參數(shù):
指明一個(gè)存放應(yīng)用程序要發(fā)送數(shù)據(jù)的緩沖區(qū)移袍;
-
第三個(gè)參數(shù):
指明實(shí)際要發(fā)送的數(shù)據(jù)的字節(jié)數(shù)解藻;
-
第四個(gè)參數(shù):
一般置0。
flag的取值和對(duì)應(yīng)的意義:
0: 與write()無異
MSG_DONTROUTE:告訴內(nèi)核葡盗,目標(biāo)主機(jī)在本地網(wǎng)絡(luò)螟左,不用查路由表
MSG_DONTWAIT:將單個(gè)I/O操作設(shè)置為非阻塞模式
MSG_OOB:指明發(fā)送的是帶外信息
send函數(shù)的執(zhí)行流程
這里是講同步的情況。
當(dāng)調(diào)用該函數(shù)時(shí)戳粒,send先比較待發(fā)送數(shù)據(jù)的長度len和套接字s的發(fā)送緩沖區(qū)的長度路狮,如果len大于s的發(fā)送緩沖區(qū)的長度,該函數(shù)返回SOCKET_ERROR蔚约;
如果len小于或者等于s的發(fā)送緩沖區(qū)的長度奄妨,那么send先檢查協(xié)議是否正在發(fā)送s的發(fā)送緩沖中的數(shù)據(jù),如果是就等待協(xié)議把數(shù)據(jù)發(fā)送完苹祟;
-
如果協(xié)議還沒有開始發(fā)送s的發(fā)送緩沖中的數(shù)據(jù)或者s的發(fā)送緩沖中沒有數(shù)據(jù)砸抛,那么send就比較s的發(fā)送緩沖區(qū)的剩余空間和len,如果len大于剩余空間大小send就一直等待協(xié)議把s的發(fā)送緩沖中的數(shù)據(jù)發(fā)送完树枫,如果len小于剩余空間大小send就僅僅把buf中的數(shù)據(jù)copy到剩余空間里直焙。
(注意并不是send把s的發(fā)送緩沖中的數(shù)據(jù)傳到連接的另一端的,而是協(xié)議傳的砂轻,send僅僅是把buf中的數(shù)據(jù)copy到s的發(fā)送緩沖區(qū)的剩余空間里)奔誓。
返回值
如果send函數(shù)copy數(shù)據(jù)成功,就返回實(shí)際copy的字節(jié)數(shù)搔涝,如果send在copy數(shù)據(jù)時(shí)出現(xiàn)錯(cuò)誤厨喂,那么send就返回SOCKET_ERROR和措;
如果send在等待協(xié)議傳送數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開的話,那么send函數(shù)也返回SOCKET_ERROR蜕煌。
send函數(shù)把buf中的數(shù)據(jù)成功copy到s的發(fā)送緩沖的剩余空間里后它就返回了派阱,但是此時(shí)這些數(shù)據(jù)并不一定馬上被傳到連接的另一端。
如果協(xié)議在后續(xù)的傳送過程中出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤的話斜纪,那么下一個(gè)Socket函數(shù)就會(huì)返回SOCKET_ERROR贫母。
每一個(gè)除send外的Socket函數(shù)在執(zhí)行的最開始總要先等待套接字的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢才能繼續(xù),如果在等待時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤盒刚,那么該Socket函數(shù)就返回SOCKET_ERROR腺劣。
在Unix系統(tǒng)下,如果send在等待協(xié)議傳送數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開的話伪冰,調(diào)用send的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào)誓酒,進(jìn)程對(duì)該信號(hào)的默認(rèn)處理是進(jìn)程終止。
6.recv
int recv(SOCKET s(指定一個(gè)接收端套接字), char FAR *buf(指定一塊接收端緩沖區(qū)), int len(緩沖區(qū)長度), int flags);
不論是客戶還是服務(wù)器應(yīng)用程序都用recv函數(shù)從TCP連接的另一端接收數(shù)據(jù)贮聂。
參數(shù)
-
第一個(gè)參數(shù):
指定接收端套接字描述符;
-
第二個(gè)參數(shù):
指明一個(gè)緩沖區(qū)寨辩,該緩沖區(qū)用來存放recv函數(shù)接收到的數(shù)據(jù)吓懈;
-
第三個(gè)參數(shù):
指明buf的長度;
-
第四個(gè)參數(shù):
一般置0
flags取值有:
0:常規(guī)操作靡狞,與read()相同
MSG_DONTWAIT:將單個(gè)I/O操作設(shè)置為非阻塞模式
MSG_OOB:指明發(fā)送的是帶外信息
MSG_PEEK:可以查看可讀的信息耻警,在接收數(shù)據(jù)后不會(huì)將這些數(shù)據(jù)丟失
MSG_WAITALL:通知內(nèi)核直到讀到請求的數(shù)據(jù)字節(jié)數(shù)時(shí),才返回甸怕。
recv的執(zhí)行流程 & 返回值
這里只描述同步recv函數(shù)的執(zhí)行流程甘穿。
當(dāng)應(yīng)用程序調(diào)用recv函數(shù)時(shí),recv先等待s的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢梢杭,如果協(xié)議在傳送s的發(fā)送緩沖中的數(shù)據(jù)時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤温兼,那么recv函數(shù)返回SOCKET_ERROR.
如果s的發(fā)送緩沖中沒有數(shù)據(jù)或者數(shù)據(jù)被協(xié)議成功發(fā)送完畢后,recv先檢查套接字s的接收緩沖區(qū)武契,如果s接收緩沖區(qū)中沒有數(shù)據(jù)或者協(xié)議正在接收數(shù)據(jù)募判,那么recv就一直等待,只到協(xié)議把數(shù)據(jù)接收完畢咒唆。
當(dāng)協(xié)議把數(shù)據(jù)接收完畢届垫,recv函數(shù)就把s的接收緩沖中的數(shù)據(jù)copy到buf中(注意協(xié)議接收到的數(shù)據(jù)可能大于buf的長度,所以在這種情況下要調(diào)用幾次recv函數(shù)才能把s的接收緩沖中的數(shù)據(jù)copy完全释。recv函數(shù)僅僅是copy數(shù)據(jù)装处,真正的接收數(shù)據(jù)是協(xié)議來完成的),recv函數(shù)返回其實(shí)際copy的字節(jié)數(shù)浸船。
如果recv在copy時(shí)出錯(cuò)妄迁,那么它返回SOCKET_ERROR找前;如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)中斷了,那么它返回0判族。
在Unix系統(tǒng)下躺盛,如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開了,那么調(diào)用recv的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào)形帮,進(jìn)程對(duì)該信號(hào)的默認(rèn)處理是進(jìn)程終止槽惫。
7.bind
int bind(SOCKET sockfd(要綁定的socket的描述符), const struct sockaddr FAR *name(該指針指定一個(gè)sockaddr結(jié)構(gòu)), int namelen(被指定的結(jié)構(gòu)的大小));
當(dāng)創(chuàng)建了一個(gè)Socket以后辩撑,套接字?jǐn)?shù)據(jù)結(jié)構(gòu)中有一個(gè)默認(rèn)的IP地址和默認(rèn)的端口號(hào)界斜。
服務(wù)程序調(diào)用bind
函數(shù)來給其綁定一個(gè)IP地址和一個(gè)特定的端口號(hào)『霞剑客戶程序一般不必調(diào)用bind
函數(shù)來為其Socket綁定IP地址和斷口號(hào)各薇。
參數(shù)
-
第一個(gè)參數(shù):
指定待綁定的Socket描述符;
-
第二個(gè)參數(shù):
指定一個(gè)sockaddr結(jié)構(gòu)君躺,該結(jié)構(gòu)是這樣定義的:
struct sockaddr {
u_short sa_family;//協(xié)議族
char sa_data[14]; //socket地址
};
sa_family
指定地址族峭判,對(duì)于TCP/IP協(xié)議族的套接字,給其置AF_INET棕叫。
當(dāng)對(duì)TCP/IP協(xié)議族的套接字進(jìn)行綁定時(shí)林螃,我們通常使用另一個(gè)地址結(jié)構(gòu):
struct sockaddr_in {
short sin_family; //協(xié)議族,置AF_INET
u_short sin_port; //端口號(hào)
struct in_addr sin_addr; //sin_addr中只有一個(gè)唯一字段s_addr俺泣,表示IP地址
char sin_zero[8];
};
sin_family
置AF_INET
sin_port
指明端口號(hào)
sin_addr
結(jié)構(gòu)體中只有一個(gè)唯一的字段s_addr
疗认,表示IP地址,該字段是一個(gè)整數(shù)伏钠,一般用函數(shù)inet_addr()
把字符串形式的IP地址轉(zhuǎn)換成unsigned long型的整數(shù)值后再置給s_addr
横漏。
有的服務(wù)器是多宿主機(jī),至少有兩個(gè)網(wǎng)卡熟掂,那么運(yùn)行在這樣的服務(wù)器上的服務(wù)程序在為其Socket綁定IP地址時(shí)可以把htonl(INADDR_ANY)
置給s_addr
缎浇,這樣做的好處是不論哪個(gè)網(wǎng)段上的客戶程序都能與該服務(wù)程序通信;如果只給運(yùn)行在多宿主機(jī)上的服務(wù)程序的Socket綁定一個(gè)固定的IP地址打掘,那么就只有與該IP地址處于同一個(gè)網(wǎng)段上的客戶程序才能與該服務(wù)程序通信华畏。
htonl函數(shù)的內(nèi)部實(shí)現(xiàn)原理是這樣,先判斷主機(jī)是什么模式存儲(chǔ)尊蚁,如果是大端模式亡笑,就跟網(wǎng)絡(luò)字節(jié)序一致,直接返回參數(shù)即可横朋,如果是小端模式仑乌,則把形參轉(zhuǎn)換成大端模式存儲(chǔ)在一個(gè)臨時(shí)參數(shù)內(nèi),再把臨時(shí)參數(shù)返回;
詳見:https://blog.csdn.net/zhangdawei5A504/article/details/45747937
我們用0來填充sin_zero
數(shù)組晰甚,目的是讓sockaddr_in
結(jié)構(gòu)的大小與sockaddr
結(jié)構(gòu)的大小一致衙传。
實(shí)例
下面是一個(gè)bind
函數(shù)調(diào)用的例子:
struct sockaddr_in saddr;
saddr.sin_family = AF_INET; //協(xié)議族為AF_INET
saddr.sin_port = htons(8888); //端口號(hào)8888
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr))厕九;
8.listen
int listen( SOCKET s(將s置于監(jiān)聽狀態(tài)), int backlog(最多可以容納的客戶連接請求數(shù)));
服務(wù)程序調(diào)用listen函數(shù)使其流套接字s處于監(jiān)聽狀態(tài)蓖捶。
處于監(jiān)聽狀態(tài)的流套接字s將維護(hù)一個(gè)客戶連接請求隊(duì)列,該隊(duì)列最多容納backlog個(gè)客戶連接請求扁远。
假如該函數(shù)執(zhí)行成功俊鱼,則返回0;如果執(zhí)行失敗畅买,則返回SOCKET_ERROR并闲。
9.accept
SOCKET accept(SOCKET s(指定一個(gè)處于監(jiān)聽狀態(tài)的流套接字), struct sockaddr FAR *addr(用于返回新創(chuàng)建的套接字的地址結(jié)構(gòu)), int FAR *addrlen(用于返回新創(chuàng)建的套接字的地址結(jié)構(gòu)的長度));
服務(wù)程序調(diào)用accept函數(shù)從處于監(jiān)聽狀態(tài)的流套接字s的客戶連接請求隊(duì)列中取出排在最前的一個(gè)客戶請求,并且創(chuàng)建一個(gè)新的套接字來與客戶套接字創(chuàng)建連接通道谷羞。
參數(shù)
-
第一個(gè)參數(shù):
指定處于監(jiān)聽狀態(tài)的流套接字
-
第二個(gè)參數(shù):
操作系統(tǒng)利用第二個(gè)參數(shù)來返回新創(chuàng)建的套接字的地址結(jié)構(gòu)
-
第三個(gè)參數(shù):
操作系統(tǒng)利用第三個(gè)參數(shù)來返回新創(chuàng)建的套接字的地址結(jié)構(gòu)的長度
返回值
如果連接成功帝火,就返回新創(chuàng)建的套接字的描述符,以后與客戶套接字交換數(shù)據(jù)的是新創(chuàng)建的套接字湃缎;
如果失敗就返回INVALID_SOCKET犀填。
實(shí)例
一個(gè)調(diào)用accept
的例子:
struct sockaddr_in ServerSocketAddr;
int addrlen;
addrlen=sizeof(ServerSocketAddr);
ServerSocket=accept(ListenSocket,(struct sockaddr *)&ServerSocketAddr,&addrlen);
10.connect
int connect(SOCKET sockfd, const struct sockaddr FAR *addr(傳入?yún)?shù),指定服務(wù)器端地址信息雁歌,含IP地址和端口號(hào)), int namelen(sizeof(addr)大泻旰啤));
客戶程序調(diào)用connect函數(shù)會(huì)引發(fā)三次握手,使客戶的socket與監(jiān)聽于addr所指定的計(jì)算機(jī)的特定端口上的服務(wù)Socket進(jìn)行連接靠瞎。
connect詳解:https://blog.csdn.net/junjun150013652/article/details/37966901
參數(shù)
- sockdf:
socket文件描述符 - addr:
傳入?yún)?shù),指定服務(wù)器端地址信息求妹,含IP地址和端口號(hào) - addrlen:
傳入?yún)?shù),傳入sizeof(addr)大小
返回值
如果連接成功乏盐,connect返回0;
如果失敗則返回SOCKET_ERROR制恍。
實(shí)例
struct sockaddr_in daddr;
memset((void *)&daddr,0,sizeof(daddr));
daddr.sin_family=AF_INET;
daddr.sin_port=htons(8888);
daddr.sin_addr.s_addr=inet_addr("133.197.22.4");
connect(ClientSocket,(struct sockaddr *)&daddr,sizeof(daddr))父能;
一個(gè)簡單的TCP連接例子
分別對(duì)服務(wù)器程序和客戶程序建立一個(gè)項(xiàng)目,連接時(shí)先運(yùn)行服務(wù)器程序再運(yùn)行客戶端程序净神。
1.服務(wù)器程序:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
void main() {
WSADATA wsaData;
int port = 5099; //端口號(hào)
char buf[] = "Server: 我是服務(wù)器何吝。";
//套接字版本號(hào)為2.2
//調(diào)用成功會(huì)返回0
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "Failed to load Winsock" << endl;
return;
}
//創(chuàng)建用于監(jiān)聽的套接字
//AF_INET是IPv4網(wǎng)絡(luò)協(xié)議的套接字類型
//sock_stream 是有保障的面向連接的SOCKET
//某協(xié)議中只有一種特定類型時(shí),protocol參數(shù)設(shè)置為0
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(port); //1024以上的端口號(hào)
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
if (retVal == SOCKET_ERROR) {
cout << "Failed bind: " << WSAGetLastError() << endl;
return;
}
if (listen(sockSrv, 10) == SOCKET_ERROR) {
printf("Listen failed:%d", WSAGetLastError());
return;
}
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
//等待客戶請求到來
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len);
if (sockConn == SOCKET_ERROR) {
printf("Accept failed:%d", WSAGetLastError());
//break;
}
printf("Accept client IP:[%s]\n", inet_ntoa(addrClient.sin_addr));
//發(fā)送數(shù)據(jù)
int iSend = send(sockConn, buf, sizeof(buf), 0);
if (iSend == SOCKET_ERROR) {
printf("send failed");
// break;
}
char recvBuf[100];
memset(recvBuf, 0, sizeof(recvBuf));
//接收數(shù)據(jù)
recv(sockConn, recvBuf, sizeof(recvBuf), 0);
printf("%s\n", recvBuf);
closesocket(sockConn);
closesocket(sockSrv);
WSACleanup();
}
2.客戶端程序
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void main()
{
//加載套接字
WSADATA wsaData;
char buff[1024];
memset(buff, 0, sizeof(buff));
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Failed to load Winsock");
return;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(5099);
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//創(chuàng)建套接字
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
if (SOCKET_ERROR == sockClient) {
printf("Socket() error:%d", WSAGetLastError());
return;
}
//向服務(wù)器發(fā)出連接請求
if (connect(sockClient, (struct sockaddr*) & addrSrv, sizeof(addrSrv)) == INVALID_SOCKET) {
printf("Connect failed:%d", WSAGetLastError());
return;
}
else
{
//接收數(shù)據(jù)
recv(sockClient, buff, sizeof(buff), 0);
printf("%s\n", buff);
}
//發(fā)送數(shù)據(jù)
char buffSend[] = "Client: 我是客戶鹃唯。";
send(sockClient, buffSend, strlen(buffSend) + 1, 0);
printf("%d", strlen(buffSend) + 1);
//關(guān)閉套接字
closesocket(sockClient);
WSACleanup();
system("pause");
}
運(yùn)行結(jié)果
這樣就顯示TCP連接成功了爱榕。