1. 程式人生 > >socket實現TCP通訊

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;
}

例如: