【轉(zhuǎn)載】Windows Socket API(WSA)函數(shù) & 簡單的TCP連接實(shí)現(xiàn)

原文:
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é)果

image-20200524171023483.png

這樣就顯示TCP連接成功了爱榕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市坡慌,隨后出現(xiàn)的幾起案子黔酥,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跪者,死亡現(xiàn)場離奇詭異棵帽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)渣玲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門逗概,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忘衍,你說我怎么就攤上這事逾苫。” “怎么了淑履?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵隶垮,是天一觀的道長。 經(jīng)常有香客問我秘噪,道長狸吞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任指煎,我火速辦了婚禮蹋偏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘至壤。我一直安慰自己威始,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布像街。 她就那樣靜靜地躺著黎棠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镰绎。 梳的紋絲不亂的頭發(fā)上脓斩,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音畴栖,去河邊找鬼随静。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吗讶,可吹牛的內(nèi)容都是我干的燎猛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼照皆,長吁一口氣:“原來是場噩夢啊……” “哼重绷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纵寝,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤论寨,失蹤者是張志新(化名)和其女友劉穎星立,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葬凳,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绰垂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了火焰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劲装。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖昌简,靈堂內(nèi)的尸體忽然破棺而出占业,到底是詐尸還是另有隱情,我是刑警寧澤纯赎,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布谦疾,位于F島的核電站,受9級(jí)特大地震影響犬金,放射性物質(zhì)發(fā)生泄漏念恍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一晚顷、第九天 我趴在偏房一處隱蔽的房頂上張望峰伙。 院中可真熱鬧,春花似錦该默、人聲如沸瞳氓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匣摘。三九已至,卻和暖如春裹刮,著一層夾襖步出監(jiān)牢的瞬間恋沃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工必指, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恕洲。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓塔橡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親霜第。 傳聞我的和親對(duì)象是個(gè)殘疾皇子葛家,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355