winsock入门之建立socket

来源:互联网 发布:淘宝号申请注册 编辑:程序博客网 时间:2024/06/08 11:25

不只是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(){// 初始化WinsockWSADATA wsaData;int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (iResult != NO_ERROR){printf_s("failed to initialize!\n");return 0;}// 建立socketSOCKET 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);//bindif (bind(m_socketListen, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR){printf_s("failed to bind!\n");closesocket(m_socketListen);WSACleanup();return 0;}//listenif (listen(m_socketListen, 1) == SOCKET_ERROR) {printf_s("listen failed\n");closesocket(m_socketListen);WSACleanup();return 0;}//accept//从监听socket上接受一个等待的连接请求后,accept()函数为新创建的连接返回一个新的socketSOCKET 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 0