socket實現TCP通訊
TCP是一種可靠的,面向連線的協議。
在socket中,建立TCP連線的過程大致如下:
伺服器端:
1.初始化套接字
2.建立伺服器socket
3.將本機地址與伺服器socket繫結在一起
4.伺服器開始監聽
5.伺服器確認請求(確認之前時一種阻塞的狀態),accept()函式生成一個新的socket。
6.用新生成的socket接收資料
客戶端:
1.初始化套接字
2.生成與伺服器連線的socket
3.將socket與伺服器進行連線
4.向伺服器傳送資料
以上過程需要用到以下一些函式:
1.SOCKET socket(int 地址族,int 套接字型別,int 協議型別),返回生成的SOCKET
2.int bind(SOCKET 要繫結的套接字,const sockaddr* 要繫結的地址的結構體的指標,int 第二個引數的長度)
將套接字繫結到指定的網路地址上,一般在connect()或listen()函式前呼叫。在伺服器端,用作監聽客戶端連線請求的套接字一定要經過繫結。
在客戶端使用的套接字一般不必繫結,除非要指定它使用特定的網路地址。
3. int listen(SOCKET 要監聽的套接字,int 最大允許等待連線長度)
適用於支援連線的套接字。僅用於伺服器。
4.SOCKET accept(SOCKET 監聽套接字,sockaddr* 客戶端的地址結構,int* 客戶端地址長度)
accept函式將從listen的等待佇列中抽取出一個項,建立TCP連線,返回一個已經經過了連線的新的套接字。
若佇列中沒有連線請求,當:
阻塞方式時,該函式阻塞呼叫它的程序。
非阻塞方式時,該函式返回一個錯誤程式碼。
5.int connect(SOCKET s,const sockaddr* 伺服器端地址結構,int 伺服器端地址長度)
用於客戶端請求向伺服器端建立連線。
6.int recv(SOCKET 已建立連線的套接字,char* 接收字串的緩衝區,int 緩衝區長度,int 選項一般為0)
返回實際接收的位元組數
7.int send(SOCKET 已建立連線的套接字,char* 傳送字串的緩衝區,int 緩衝區長度,int 選項一般為0)
返回實際傳送的位元組數
原始碼如下:
伺服器端:
#include "stdafx.h"
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"WS2_32.lib")
#include <string.h>
#define bufsize 64
int main()
{
WSADATA wsd;
SOCKET sServer, sClient;
int retval;
char recvbuf[bufsize];
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("startup failed\n");
WSACleanup();
return -1;
}
sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sServer == INVALID_SOCKET)
{
printf("socket failed\n");
WSACleanup();
return -1;
}
sockaddr_in addrServ;
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(9000);
addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//INADDR_ANY是IP地址0.0.0.0,表示來自任意IP地址的請求均接收
retval = bind(sServer, (sockaddr*)&addrServ, sizeof(sockaddr_in));
if (retval == SOCKET_ERROR)
{
printf("bind failed\n");
WSACleanup();
return -1;
}
retval = listen(sServer, 1);
if (retval == SOCKET_ERROR)
{
printf("listen failed\n");
closesocket(sServer);
WSACleanup();
return -1;
}
printf("TCP server start...\n");//至此,伺服器啟動完成
sockaddr_in ClientAddr;
int addrlen;
addrlen = sizeof(ClientAddr);
sClient = accept(sServer, (sockaddr*)&ClientAddr, &addrlen);
//阻塞狀態
if (sClient == INVALID_SOCKET)
{
printf("accept failed\n");
closesocket(sServer);
WSACleanup();
return -1;
}
printf("client has log in\n");//開啟客戶端時,顯示客戶已登入
while (true)
{
memset(recvbuf, 0, sizeof(recvbuf));
retval = recv(sClient, recvbuf, BUFSIZ, 0);
if (retval == SOCKET_ERROR)
{
printf("recv failed\n");
closesocket(sClient);
closesocket(sServer);
WSACleanup();
break;
}
printf("ip: %s\nport: %d\ncontent: %s\n", inet_ntoa(ClientAddr.sin_addr), ClientAddr.sin_port, recvbuf);
}
return 0;
}
客戶端:
#include "stdafx.h"
#include <winsock2.h>
#include <string>
#include <string.h>
#include <iostream>
#include <stdio.h>
#pragma comment(lib,"WS2_32.lib")
#define bufsize 64
int main()
{
WSADATA wsd;
SOCKET sHost;
sockaddr_in servaddr;
char buf[bufsize];
int retval;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("stratup failed\n");
return -1;
}
sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sHost == INVALID_SOCKET)
{
printf("socket failed\n");
WSACleanup();
return -1;
}
servaddr.sin_family = AF_INET;
servaddr.sin_addr.S_un.S_addr = inet_addr("10.21.38.14");//伺服器IP地址;
servaddr.sin_port = htons(9000);
int addrlen;
addrlen = sizeof(servaddr);
retval = connect(sHost, (sockaddr*)&servaddr, addrlen);
if (retval == SOCKET_ERROR)
{
printf("connect error\n");
closesocket(sHost);
WSACleanup();
return -1;
}
while (1)
{
printf("input the message:\n");
std::string str;
std::getline(std::cin, str);
memset(buf, 0, sizeof(buf));
strcpy(buf, str.c_str());
retval = send(sHost, buf, strlen(buf), 0);
if (retval == SOCKET_ERROR)
{
printf("send failed\n");
closesocket(sHost);
WSACleanup();
return -1;
}
}
return 0;
}
例如: