winsock入门之建立socket

不只是winsock,几乎所有网络程序可以分为5个步骤:

1.      打开socket

2.      命名socket

3.      与另一个socket建立关联

4.      与socket之间发送和接收数据

5.      关闭socket

(一)下面主要说明连接的建立过程。

打开socket:int socket(intaf, int type, int protocol);

参数说明:

af: 地址族的类型AF_UNIXAF_INET

type: 数据格式:流SOCK_STREAM/报文SOCK_DGRAM

protocol: 协议类型

命名socket:intbind(SOCKET s, struct sockaddr FAR *addr, int namelen);

参数说明:

s:socket句柄。

addr:指向socket地址结构的指针。

namelen:addr所指向的socket结构的长度。

这里用到的一个数据结构sockaddr_in是给socket命名的关键,具体如下:

struct sockaddr_in{

         short         sin_family;//地址族

         u_short    sin_port;//端口号

         struct       in_addr    sin_addr;//地址

         char          sin_zero[8];//没用

};//这里当然都是绑定本机地址127.0.0.1

第1、2步是服务器和客户端都要做的,但是一般情况下客户端为了防止端口号冲突会采用隐式命名的方式,在向服务器发起连接的时候(调用connect()函数)协议栈会分配给客户端独有的端口号。

3.1.服务器监听客户端

int PASCAL FAR listen(SOCKETs, int backlog);

3.2.客户端发起连接

int PASCAL FAR connet(SOCKETs, struct sokaddr FAR *addr, int namelen);

3.3.服务器完成连接

从监听socket上接受一个等待的连接请求后,accept()函数为新创建的连接返回一个新的socket。

SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR *addr, int FAR*addrlen);

(二)接下来就是发送数据和关闭套接字了。

这里我实现了两个功能:发送消息和发送文件。

客户端首先选择发送类型:1.消息;2.文件

两种方式实现起来没有大差别,只是在发送文件的时候首先发送一个标志FILE_FLAG " xmzulesile "提醒服务器将要发送文件,然后服务器将接收到的字节流写入文件,客户端发送文件结束后发送一个标志FILE_SEND_OVER "over",服务器收到后知道发送结束给客户端发送”Received!”。

最后说明一个问题:这里可以说使用了停-等协议,发送一个数据包收到确认再发送下一个数据包,否则客户端连续发送完所有数据包(包括结束标志),服务器收不到结束标志,就会一直阻塞下去。具体原因还没想明白,一开始没有加"接收确认"的时候,调试是没问题的,但是运行就会阻塞。

上面就是工作流程,结束后就可以关闭套接字了。

附上源代码:

客户端:

#include <stdio.h>
#include <winsock2.h>
#include <tchar.h>
#pragma comment(lib, "ws2_32.lib") //加载库文件

char FILE_FLAG[12] = "xmzulesile"; // 发送文件的标志
char FILE_SEND_OVER[10] = "over";   // 发送文件结束的标志

int main()
{
	// 初始化Winsock
	WSADATA wsaData;
	int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != NO_ERROR)
	{
		printf_s("failed to initialize!\n");
		return 0;
	}

	// 建立socket
	SOCKET m_socketServer;// 连接服务器的套接字
	m_socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_socketServer == INVALID_SOCKET)
	{
		printf_s("failed to create client socket!\n");
		WSACleanup();
		return 0;
	}

	sockaddr_in clientAddr;
	clientAddr.sin_family = AF_INET;
	clientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	clientAddr.sin_port = htons(3579);

	// 连接到服务器
	if (connect(m_socketServer, (SOCKADDR*)&clientAddr, sizeof(clientAddr)) == SOCKET_ERROR)
	{
		printf_s("failed to connect\n");
		closesocket(m_socketServer);
		WSACleanup();
		return 0;
	}

	char c;
	char buf[1024 + 1] = { 0 };
	char filename[255];
	FILE *fp = NULL;

	//send(m_socketServer, buf, strlen(buf), 0);
	// 发送数据
	while (TRUE)
	{
		printf_s("发送:1.消息;2.文件\n");
		if ((c = getchar()) == '1')
		{
			getchar();// 丢弃回车符
			gets_s(buf, 1024);// 读取消息
			send(m_socketServer, buf, strlen(buf), 0);
		}
		else if (c == '2')
		{
			getchar();// 丢弃回车符
			printf_s("请输入本地文件路径: ");
			scanf_s("%s",&filename, 255);
			getchar();// 丢弃回车符
			if (fopen_s(&fp, filename, "rb+"))
			{
				::MessageBox(NULL, _T("failed to open file in client"), NULL, 0);
				return 0;
			}
			send(m_socketServer, FILE_FLAG, sizeof(FILE_FLAG), 0);//通知服务器将要发送文件
			// 读取文件
			int i = fread(buf, 1, 1024, fp);
			while (i > 0)
			{
				//这里可以说使用停-等协议,发送一个数据包收到确认再发送下一个数据包
				//否则客户端连续发送完所有数据包(包括结束标志),服务器收不到结束标志,就会一直阻塞下去
				//具体原因还没想明白,一开始没有加"接收确认"的时候,调试是没问题的,但是运行就会阻塞
				send(m_socketServer, buf, i, 0);
				recv(m_socketServer, buf, 1024, 0);//接收确认
				SecureZeroMemory(buf, sizeof(buf));
				i = fread(buf, 1, 1024, fp);
			}
			if (ferror(fp))
			{
				MessageBox(NULL, _T("fread error"), NULL, 0);
				return 0;
			}
			//告诉服务器发送完毕
			send(m_socketServer, FILE_SEND_OVER, sizeof(FILE_SEND_OVER), 0);
			//接收通知(服务器接收完毕)
			int ret = recv(m_socketServer, buf, 1024, 0);
			if (ret == 0)
			{
				printf("Recv data error: %d\n", WSAGetLastError());
				return 0;
			}
			if (buf[0])//输出服务器发过来的消息
			{
				buf[ret] = 0;
				printf_s("Server:%s\n",  buf);
			}
			if (fp)
			{
				fclose(fp);//关闭文件
				fp = NULL;
			}
		}
		else
		{
			getchar();
			printf_s("incorrect input\n");
		}
	}

	closesocket(m_socketServer);
	WSACleanup();
	return 0;
}


服务器:
#include <WinSock2.h>
#include <iostream>
#include <string.h>
#include <tchar.h>
#pragma comment(lib, "ws2_32.lib")

char FILE_FLAG[12] = "xmzulesile"; // 客户端发来文件的标志
char FILE_SEND_OVER[10] = "over";    // 客户端发送文件结束的标志

//接收文件
unsigned recvFile(SOCKET s, const char *filename)
{
	FILE *fp;
	char buf[1024] = { 0 };
	int ret = 0;
	if (fopen_s(&fp, filename, "wb+"))
	{
		::MessageBox(NULL, _T("failed to write file in server"), NULL, 0);
		return 0;
	}
	do
	{
		//接收数据
		ret = recv(s, buf, 1024, 0);
		if (ret == 0)
		{
			printf("Recv data error: %d\n", WSAGetLastError());
			return 0;
		}
		//是否接收结束
		if (!strcmp(buf, FILE_SEND_OVER))
		{
			send(s, "Received!", 10, 0);//通知发送端全部接收完毕
			break;
		}
		else
		{
			//把数据写入打开的文件
			fwrite(buf, 1, ret, fp);
			send(s, "Got it", 10, 0);//发送确认
			SecureZeroMemory(buf, sizeof(buf));
		}
	} while (ret > 0);

	if (fp)
	{
		fclose(fp);
		fp = NULL;
	}
	return 0;
}

int main()
{
	WSADATA wsaData;
	int iInit = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iInit != NO_ERROR)
	{
		printf_s("failed to initialize!\n");
		return 0;
	}

	SOCKET m_socketListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_socketListen == INVALID_SOCKET)
	{
		printf_s("failed to create server socket!\n");
		WSACleanup();
		return 0;
	}

	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	serverAddr.sin_port = htons(3579);

	//bind
	if (bind(m_socketListen, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		printf_s("failed to bind!\n");
		closesocket(m_socketListen);
		WSACleanup();
		return 0;
	}

	//listen
	if (listen(m_socketListen, 1) == SOCKET_ERROR) {
		printf_s("listen failed\n");
		closesocket(m_socketListen);
		WSACleanup();
		return 0;
	}

	//accept
	//从监听socket上接受一个等待的连接请求后,accept()函数为新创建的连接返回一个新的socket
	SOCKET m_socketClient = accept(m_socketListen, NULL, NULL);// 跟客户端连接的套接字
	if (m_socketClient == INVALID_SOCKET) {
		wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
		closesocket(m_socketListen);
		WSACleanup();
		return 0;
	}

	int bytes = SOCKET_ERROR;
	char buf[1024] = { 0 };

	//接收数据
	while (TRUE)
	{
		if((bytes = recv(m_socketClient, buf, 1024, 0)) == 0)
		{
			break;
		}
		if (!strcmp(FILE_FLAG, buf))
		{
			printf_s("Here comes a file\n");
			recvFile(m_socketClient, "d:\\recv");
		}
		else
		{
			buf[bytes] = '\0';// 字符串结尾标志
			printf_s("Client:%s\n", buf);
		}
	}

	closesocket(m_socketListen);
	WSACleanup();
	return 0;

}


  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值