上一篇讲的是简单的发送数据的客户端的实现。接下来讲的是如何实现收发数据服务器。
这里说的服务器其实就是一个进程,它需要等待任意数量的客户端与之建立起连接,以便响应它们的请求。

服务器必须在已知的名称上监听连接(在TCP/IP中是ip地址和端口号,不同协议的寻址方案和命名方法也不同)

创建服务器程序的第一步和客户端一样,要先初始化并且创建SOCKET(LISTENSOCKET用于监听客户端连接)

#define DEFAULT_PORT "27015"

struct addrinfo *result = NULL, *ptr = NULL, hints;

ZeroMemory(&hints, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE; // Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}

  

这里的套接字设置的监听地址为AF_INET,也就是IPv4的地址。如果想监听IPv6的地址就可以设置为AF_INET6,如果想同时监听的话就需要创建两个监听套接字,(vista系统之后提供了一个特殊的套接字能够同时监听IPv4和IPv6。详情: Dual-Stack Sockets

接下来需要将套接字绑定到它已知的地址(IP和端口)

iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);

  

bind函数:

int bind(
_In_ SOCKET s, //等待客户端连接的套接字
_In_ const struct sockaddr *name, // 指向进行绑定的地址结构 
_In_ int namelen //表示要传递的,由协议决定的地址结构的长度
);

一旦绑定出错,bind回返回SOCKET_ERROR。对bind而言,最常见的错误是WSAEADDRINUSE(表示另一进程已经同本地ip及端口号绑定,或者该ip和端口号处于TIME——WAIT状态)和WSAEFAULT(调用的套接字已经被绑定)

bind函数被调用之后,getaddrinfo函数返回的地址信息就基本没有什么作用了,可以使用freeaddrinfo释放地址信息。

接下来要做的是将套接字设置为监听模式:

if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
printf( "Listen failed with error: %ld\n", WSAGetLastError() );
closesocket(ListenSocket);
WSACleanup();
return 1;
}

  

listen函数:

int listen(
_In_ SOCKET s, // 被绑定的套接字
_In_ int backlog // 监听队列中允许保持的尚未处理的最大连接数量
);

backlog这个参数很重要,它指定了被搁置的连接的最大队列长度(比如backlog参数为3,但是有4台客户端同时发出请求,那么前3个会被放在挂起队列之中,而第四个会连接请求失败返回WSAECONNREFUSED错误)

现在可以接受客服端的连接了:

SOCKET ClientSocket;

ClientSocket = INVALID_SOCKET;

ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}

  

accept函数会返回一个新的套接字描述符,它对应已经接受了客户端的连接,该客户端的后续操作都用这个新的套接字,而原来LinstenSocket仍然处于监听模式:

SOCKET accept(
_In_ SOCKET s, //一个出于监听模式的套接字
_Out_ struct sockaddr *addr, //一个指向sockaddr_in结构的指针,用于取得对方的地址信息
_Inout_ int *addrlen //addr结构的长度
);

接受客户端的连接之后,就使用RECV和SEND函数接收发送数据了

#define DEFAULT_BUFLEN 512

char recvbuf[DEFAULT_BUFLEN];
int iResult, iSendResult;
int recvbuflen = DEFAULT_BUFLEN; do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
} else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
} } while (iResult > 0);

  

通信完毕,断开连接:

iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
closesocket(ClientSocket);
WSACleanup();

  

完整的server源代码:

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <IPHlpApi.h>
#include <stdio.h> #pragma comment(lib, "Ws2_32.lib")
//#pragma comment(lib, "Mssock.lib")
//#pragma comment(lib, "Advapi32.lib") #define DEFAULT_PORT "27015"
#define DeFAULT_BUFLEN 512
int main()
{
WSADATA wsaData;
int iResult;
int iSendResult;
struct addrinfo *result = NULL;
struct addrinfo hints;
char recvbuf[DeFAULT_BUFLEN];
int recvbuflen = DeFAULT_BUFLEN;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET; iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
} ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE; // 为服务器确定本地地址和端口
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
} ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET)
{
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
} iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result); iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR)
{
printf("Listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
} ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET)
{
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
closesocket(ListenSocket); do
{
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
{
printf("Bytes received: %d\n", iResult);
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
{
printf("Connection closing...\n");
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0); // 断开连接
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// 清理连接
printf("recv message: %s", recvbuf);
closesocket(ClientSocket);
WSACleanup(); return 0;
}

  

原文链接: http://www.bugcoding.com/entry/11

WinSock网络编程基础(3)server的更多相关文章

  1. WinSock网络编程基础(2)客户端

    接下来说一下如何用WinSock创建基于TCP/IP模型的客户端和服务器. TCP可以提供两个计算机间可靠无误的数据传输,应用程序使用TCP通信时,会在两台计算机之间建立一个虚拟连接,连接之后计算机之 ...

  2. WinSock网络编程基础(1)

    记录学习windows网络编程过程中遇到的问题和相关笔记 基本概念: Socket: socket起源于UNIX,Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.基于&qu ...

  3. 【网络编程1】网络编程基础-TCP、UDP编程

    网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...

  4. iOS开发网络篇—网络编程基础

    iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过 ...

  5. Android 网络编程基础之简单聊天程序

    前一篇讲了Android的网络编程基础,今天写了一个简单的聊天程序分享一下 首先是服务端代码: package com.jiao.socketdemo; import java.io.Buffered ...

  6. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  7. python全栈开发从入门到放弃之socket网络编程基础

    网络编程基础 一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务 ...

  8. 网络编程基础之C/S架构和TCP/IP协议

    一.何谓C/S架构 C指的是client(客户端软件),S指的是Server(服务端软件),既然我们的的标题是网络编程基础, 那我们就一起来学习怎样写一个C/S架构的软件,实现服务端与客户端软件基于网 ...

  9. 第5章 Linux网络编程基础

    第5章 Linux网络编程基础 5.1 socket地址与API 一.理解字节序 主机字节序一般为小端字节序.网络字节序一般为大端字节序.当格式化的数据在两台使用了不同字节序的主机之间直接传递时,接收 ...

随机推荐

  1. linux 命令之 uptime

    uptime 命令是用来查询linux系统负载的. 命令格式 uptime [OPTION] -V 显示版本号 不带參数的 uptime 直接输出系统负载. 何为系统负载呢? 系统平均负载被定义为在特 ...

  2. php,ajax登陆退出

    利用ajax可以做到页面无刷新登陆. 运行效果 目录结构 site/ css/ images/ js/ site/css/bootstrap.css(bootstrap样式表) site/js/boo ...

  3. bootstrap注意事项(七)图片

    在本章中,我们将学习 Bootstrap 对图片的支持.Bootstrap 提供了三个可对图片应用简单样式的 class: .img-rounded:添加 border-radius:6px 来获得图 ...

  4. 图片处理之-Bitmap.Config,jpeg压缩与大小

    关于ARGB_8888.ALPHA_8.ARGB_4444.RGB_565的理解 A:透明度 R:红色 G:绿 B:蓝 Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4 ...

  5. DEV控件自定义排序实现

    一般的控件或者组件都支持按照某一列进行排序.但是,这种排序是根据数据源里的数据默认按照降序或升序排序的,同时这样的排序与字段的类型有关. 假设现在字段的类型是字符串类型 ,但是,存储的数据时数字加一些 ...

  6. Programming C#.Inheritance and Polymorphism

    继承 C#中,创建派生类要在派生类的名字后面加上冒号,后面再跟上基类的名字: public class ListBox : Control 提示:C++程序员注意了,C#没有私有或者保护继承 多态 继 ...

  7. 怎样在超级终端和PC之间通过串口传输文件

    Windows环境下,通过SecureCRT软件,用串口向ARM开发板发送文件: 输入命令 rz,可以看到如下图所示: 选择路径点击上传即可. 如果是想从Arm开发板中把文件Down下来,则可以按照下 ...

  8. 添加nginx为系统服务(service nginx start/stop/restart)

    1.在/etc/init.d/目录下编写脚本,名为nginx #!/bin/sh # # nginx - this script starts and stops the nginx daemon # ...

  9. Java生成缩略图之Thumbnailator

    Thumbnailator 是一个为Java界面更流畅的缩略图生成库.从API提供现有的图像文件和图像对象的缩略图中简化了缩略过程,两三行代码就能够从现有图片生成缩略图,且允许微调缩略图生成,同时保持 ...

  10. [问题解决] initAndListen: 10309 Unable to create/open lock file: /data/db/mongod.lock

    错误: 在linux下开启mongoDB的 $ >bin: ./mongod 时报错:initAndListen: 10309 Unable to create/open lock file: ...