套接字編程入門

代碼中使用到的API參見官網:https://docs.microsoft.com/zh-cn/windows/desktop/api/winsock2/

UDP套接字關鍵步驟:

                 服務端

                 客戶端

初始化WSA、創建socket

初始化WSA、創建socket

創建地址結構:sockaddr_in

創建地址結構:sockaddr_in

綁定socket和地址結構:bind()

 

接收消息: recvfrom()

發送消息:sendto()

關閉Socket、註銷WSA

關閉Socket、註銷WSA

UDP服務端代碼如下:

//Socket服務器端代碼
#include <tchar.h>
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>   
#include <string.h>   
#include <Ws2tcpip.h>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 2048

int main(int argc, char* argv[]){
	//初始化WSA,使得程序可以調用windows socket,WSA版本指定爲2.2
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0){
		return 0;
	}

	//創建套接字,server_socket,類型是UDP
	SOCKET server_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (server_socket == INVALID_SOCKET) {
		//如果創建的socket無效,則結束程序
		perror("socket error !\n");
		return 0;
	}

	//創建地址結構,server_addr,並設置端口和IP
	sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	//端口號 8887
	server_addr.sin_port = htons(8887);
	//此處INADDR_ANY表示所有本機IP地址
	server_addr.sin_addr.S_un.S_addr = INADDR_ANY;

	//將socket與地址server_addr綁定
	if (bind(server_socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)) == SOCKET_ERROR){
		perror("bind error !\n");
		return 0;
	}

	//循環接收來自客戶端的消息
	while (1){
		// 定義客戶端的socket地址結構
		sockaddr_in client_addr;
		int client_addr_length = sizeof(client_addr);

		// recv函數接收數據到緩衝區buffer中 
		char buffer[BUFFER_SIZE];
		memset(buffer, 0, BUFFER_SIZE);
		if (recvfrom(server_socket, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &client_addr_length) == -1){
			perror("接收消息失敗:\n");
			break;
		}

		//將收到的消息輸出到命令行
		char IP_BUFFER[256];
		memset(IP_BUFFER, 0, 256);
		InetNtop(AF_INET, &client_addr.sin_addr, IP_BUFFER, 256);
		printf("從%s:%d處收到消息:%s\n", IP_BUFFER, ntohs(client_addr.sin_port), buffer);
	}

	// 關閉服務器socket 並註銷 WSA
	closesocket(server_socket);
	WSACleanup();
	system("pause");
	return 0;
}

UDP客戶端代碼如下:

//Socket客戶端代碼
//向服務器發送消息

#include <tchar.h>
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>   
#include <string.h>   
#include <Ws2tcpip.h>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 2048

int main(int argc, char* argv[]){	
	//初始化WSA,使得程序可以調用windows socket
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0)	{
		return 0;
	}

	//創建客戶端套接字,client_socket,類型是UDP
	SOCKET client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (client_socket == INVALID_SOCKET) {
		//如果創建的socket無效,則結束程序
		perror("socket error !\n");
		return 0;
	}

	//創建地址,server_addr,並設置端口和IP
	sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	//要連接的服務器端口號 8887
	server_addr.sin_port = htons(8887);
	//綁定服務器的地址127.0.0.1
	InetPton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);

	printf("請輸入發送給服務器的消息:\n");
	while (1){
		char buffer[BUFFER_SIZE];
		memset(buffer, 0, BUFFER_SIZE);
		
		// 從鍵盤中讀取輸入到BUFFER中
		gets_s(buffer, BUFFER_SIZE);

		//發送數據給服務器
		if (sendto(client_socket, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&server_addr, sizeof(SOCKADDR)) == -1){
			perror("發送消息失敗:\n");
			break;
		}
	}
	// 關閉服務器socket 並註銷 WSA
	closesocket(client_socket);
	WSACleanup();
	system("pause");
	return 0;
}

TCP套接字關鍵步驟:

服務端

客戶端

1.初始化WSA、創建socket

1.初始化WSA、創建socket

2.創建地址結構:sockaddr_in

2.創建地址結構:sockaddr_in

3.綁定socket和地址結構:bind()

3.請求連接:connect()

4.監聽 listen()

4.發送/接收消息:send()/rece()

5.通信過程:

a)接收連接請求 accept()

b)發送/接收消息 rece()/send()

c)關閉連接 closesocket()

5.關閉Socket、註銷WSA

6.關閉Socket、註銷WSA

 

 TCP服務端代碼如下:

//Socket服務器端代碼

#include <tchar.h>
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>   
#include <string.h>   
#include <Ws2tcpip.h>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 2048
#define FILE_NAME_MAX_SIZE 512 

int main(int argc, char* argv[]){
	//初始化WSA,使得程序可以調用windows socket
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0){
		return 0;
	}

	//創建監聽用套接字,server_socket,類型是TCP
	SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (server_socket == INVALID_SOCKET) {
		perror("socket error !\n");
		return 0;
	}

	//創建地址,server_addr,並設置端口和IP
	sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	//端口號 8888
	server_addr.sin_port = htons(8888);
	//INADDR_ANY表示本機任意IP地址
	server_addr.sin_addr.S_un.S_addr = INADDR_ANY;

	//將socket與地址server_addr綁定
	if (bind(server_socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)) == SOCKET_ERROR){
		perror("bind error !\n");
		return 0;
	}

	//server_socket開始監聽
	if (listen(server_socket, 20) == SOCKET_ERROR){
		perror("listen error !\n");
		return 0;
	}
	
	while (1){
		printf("等待連接...\n");

		// 定義客戶端的socket和socket地址結構
		SOCKET client_socket;
		sockaddr_in client_addr;
		int client_addr_length = sizeof(client_addr);

		// 接受連接請求,返回一個新的socket(描述符),這個新socket用於同連接的客戶端通信 
		// accept函數會把連接到的客戶端信息寫到client_addr中 
		client_socket = accept(server_socket, (SOCKADDR *)&client_addr, &client_addr_length);
		if (client_socket == INVALID_SOCKET){
			perror("Socket連接建立失敗:\n");
			continue;
		}
		
		char IP_BUFFER[256];
		memset(IP_BUFFER, 0, 256);
		InetNtop(AF_INET, &client_addr.sin_addr, IP_BUFFER,256);
		printf("Socket連接建立,客戶端IP爲:%s,端口爲:%d\n", IP_BUFFER, ntohs(client_addr.sin_port));

		//接收客戶端請求的的文件路徑
		// recv函數接收數據到緩衝區buffer中 
		char buffer[BUFFER_SIZE];
		memset(buffer,0, BUFFER_SIZE);
		if (recv(client_socket, buffer, BUFFER_SIZE, 0) < 0){
			perror("接收文件名失敗:\n");
			break;
		}

		// 然後從buffer拷貝到file_name中 
		char file_name[FILE_NAME_MAX_SIZE + 1];
		memset(file_name, 0,FILE_NAME_MAX_SIZE + 1);
		strncpy_s(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));
		
		// 打開文件並讀取文件數據 
		FILE *fp;
		errno_t F_ERR= fopen_s(&fp,file_name, "rb");
		if (F_ERR != 0){
			printf("文件打開失敗:%s\n", file_name);
		}
		else{
			printf("開始傳輸文件:%s\n", file_name);
			memset(buffer,0, BUFFER_SIZE);
			int length = 0;
			// 每讀取一段數據,便將其發送給客戶端,循環直到文件讀完爲止 
			while ((length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0){
				if (send(client_socket, buffer, length, 0) < 0){
					printf("文件發送失敗:%s/n", file_name);
					break;
				}
				memset(buffer,0, BUFFER_SIZE);
			}

			// 關閉文件 
			fclose(fp);
			printf("文件傳輸完成:%s!\n", file_name);
		}
		// 關閉與客戶端的連接 
		closesocket(client_socket);
	}
	// 關閉監聽用的socket 
	closesocket(server_socket);
	WSACleanup();
	return 0;
}

TCP客戶端代碼如下:

//Socket客戶端代碼

#include <tchar.h>
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>  
#include <string.h>  
#include <Ws2tcpip.h>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 2048
#define FILE_NAME_MAX_SIZE 512 

int main(int argc, char* argv[]){
	//初始化WSA,使得程序可以調用windows socket
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0){
		return 0;
	}

	//創建監聽用套接字,server_socket
	SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); ;
	if (client_socket == INVALID_SOCKET) {
		perror("socket error !");
		return 0;
	}

	//創建地址結構,server_addr,並設置端口和IP
	sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	//要連接的服務器端口號 8888
	server_addr.sin_port = htons(8888);
	//指定服務器的地址127.0.0.1
	InetPton(AF_INET, "127.0.0.1",&server_addr.sin_addr.s_addr);
	
	//與地址server_addr建立連接
	if (connect(client_socket, (SOCKADDR*)&server_addr, sizeof(SOCKADDR))){
		perror("connect error !\n");
		return 0;
	}

	char remote_file_name[FILE_NAME_MAX_SIZE + 1];
	memset(remote_file_name, 0, FILE_NAME_MAX_SIZE + 1);
	printf("請輸入要獲取的服務器文件路徑:\n");
	scanf_s("%s", remote_file_name, FILE_NAME_MAX_SIZE);

	char local_file_name[FILE_NAME_MAX_SIZE + 1];
	memset(local_file_name, 0, FILE_NAME_MAX_SIZE + 1);
	printf("請輸入保存文件的本地路徑:\n");
	scanf_s("%s", local_file_name, FILE_NAME_MAX_SIZE);

	char buffer[BUFFER_SIZE];
	memset(buffer,0 , BUFFER_SIZE);
	strncpy_s(buffer, remote_file_name, strlen(remote_file_name)>BUFFER_SIZE ? BUFFER_SIZE : strlen(remote_file_name));

	// 向服務器發送buffer中的數據 
	if (send(client_socket, buffer, BUFFER_SIZE, 0) < 0){
		perror("發送文件名失敗:");
		exit(1);
	}

	// 打開文件,準備寫入 
	FILE *fp;
	errno_t F_ERR = fopen_s(&fp, local_file_name, "wb");
	if (F_ERR != 0){
		printf("文件打開失敗:%s\n", local_file_name);
		exit(1);
	}

	// 從服務器接收數據到buffer中 
	// 每接收一段數據,便將其寫入文件中,循環直到文件接收完並寫完爲止 
	memset(buffer,0, BUFFER_SIZE);
	int length = 0;
	while ((length = recv(client_socket, buffer, BUFFER_SIZE, 0)) > 0){
		if (fwrite(buffer, sizeof(char), length, fp) < length){
			printf("文件寫入失敗:%s\n", local_file_name);
			break;
		}
		memset(buffer, 0, BUFFER_SIZE);
	}

	printf("\n成功從服務器接收文件\n存入本地目錄:%s\n", remote_file_name, local_file_name);

	// 接收成功後,關閉文件,關閉socket、WSA 
	fclose(fp);
	closesocket(client_socket);
	WSACleanup();
	system("pause");
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章