接下来说一下如何用WinSock创建基于TCP/IP模型的客户端和服务器。

TCP可以提供两个计算机间可靠无误的数据传输,应用程序使用TCP通信时,会在两台计算机之间建立一个虚拟连接,连接之后计算机之间变可以以双向字节流进行数据交换。

下面说下简单的发送数据的客户端实现.

创建客户机的连接比较简单:

1.创建一个套接字,定义addrinfo对象并初始化这些值(该对象包含一个sockaddr结构)

struct addrinfo *result = NULL,
*ptr = NULL,
hints; ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC; //可以是IPv4或IPv6地址
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

调用getaddrinfo函数确定服务器ip地址(由命令行参数传递)和端口

#define DEFAULT_PORT "27015"

// Resolve the server address and port
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}

socket原型:

socket(

IN int af,       //协议的地址族,使用IPv4来描述Winsock,设置为AF_INET

IN int type,     //套接字类型,TCP/IP设置为SOCK_STREAM,UDP/IP设置为SOCK_DGRAM

IN int protocol      //用于给定地址族和类型具有多重入口的传送限定,TCP设置为IPPROTO_TCP,UDP设置为IPPROTO_UDP

);

调用socket创建嵌套字,错误检测

SOCKET ConnectSocket = INVALID_SOCKET;
ptr=result;
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}

2.连接到的服务器名。(在TCP/IP中就是监听服务器的IP地址和端口号)

iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
//释放资源
freeaddrinfo(result); if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}

客户端程序在创建套节字之后,要使用 connect函数请求与服务器连接,connect原型:

int WSAAPI connect(

IN SOCKET s,            //要建立连接的socket

IN const struct sockaddr FAR * name,    //指向保存要建立连接信息的地址结构

IN int namelen          //参数2指向地址结构的大小

);

3.发送和接收数据。

收发数据才是网络编程的主题,在已经建立连接的套接字上发生数据可以使用send或WSASend(WinSock2中),接受可用recv和WSARecv。收发数据都是用char类型(面向字节的数据)。

#define DEFAULT_BUFLEN 512

int recvbuflen = DEFAULT_BUFLEN;

char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN]; int iResult; iResult = send(ConnectSocket, sendbuf, (int) strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
} printf("Bytes Sent: %ld\n", iResult); iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
} do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Bytes received: %d\n", iResult);
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);

所有收发数据返回的错误代码都是SOCKET_ERROR,一旦有错误返回,系统就会调用WSAGetLastError()获取详细错误信息。

常见错误:

WSAECONNABORTED和WSAECONNRESET:连接正在关闭(超时或者由于通信放正在关闭连接)
WSAEWOULDBLOCK:套接字处于非阻塞模式或异步状态。

使用send和recv函数原型。

int send(

SOCKET s, // 套节字句柄

const char FAR* buf,// 要发送的数据的缓冲区的地址

int len, // 缓冲区的长度

int flags  // 指定了的调用方式,通常设为0

);

int recv(

SOCKET s,

char FAR* buf,

int len,

int flags

);

send函数在一个连接的套节字上发送缓冲区内的数据,返回发送数据的实际字节数,recv函数从对方接收数据,并存储它到指定的缓冲区。flag参数在这两个函数中通常设为0。

在阻塞模式下,send将会阻塞线程的执行直到所有的数据发送完毕(或者一个错误的发生),而recv函数将返回尽可能多的当前信息,一直到缓冲区指定的大小。

函数执行失败返回INVALID_SOCKET(-1),应该调用closesocket函数将它关闭。如果没有错误发生,函数返回0,否则返回SOCKET_ERROR。函数用法如下:

int closesocket(

__in  SOCKET s // 函数唯一的参数就是要关闭的套节字的句柄

);

4.断开连接,关闭套接字(当客户机已经发出数据,可以使用shutdown函数和SD_SEND宏关闭发送套接字,客户机仍然允许接受来自服务器套接字上的数据)

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

附完整的客户端代码:

#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, "Mswsock.lib")
#pragma comment(lib, "Advapi32.lib") #define DEFAULT_PORT "27015"
#define DeFAULT_BUFLEN 512 int main(int argc, char* argv[])
{
WSADATA wsaData;
int iResult;
struct addrinfo *result = NULL,
*ptr = NULL,
hints; char *sendbuf = "this is a test";
char recvbuf[DeFAULT_BUFLEN];
int recvbuflen = DeFAULT_BUFLEN;
SOCKET ConnectSocket = INVALID_SOCKET; if (argc != 2)
{
printf("usage: %s server-name\n", argv[0]);
} // 初始化winsock
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_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// 确定服务器地址和端口
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
} // 尝试连接到服务器地址,直到成功
for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
{
// 创建套接字连接到服务器
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 连接服务器
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
} // 释放资源
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
} // 发送sendbuf内容
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf) + 1, 0);
if (iResult == SOCKET_ERROR)
{
printf("send failed:%d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Byte Send:%ld\n", iResult); // 数据发送完成之后断开连接
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown faild: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
} // 收到回应之后关闭连接
do
{
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Byte received: %d\n", iResult);
else if (iResult == 0)
printf("connection closed\n");
else
printf("recv failed: %d\n", WSACleanup()); } while (iResult > 0); // 清除套接字
closesocket(ConnectSocket);
WSACleanup(); return 0;
}

  本文链接: http://www.bugcoding.com/entry/10

WinSock网络编程基础(2)客户端的更多相关文章

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

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

  2. WinSock网络编程基础(3)server

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

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

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

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

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

  5. C#网络编程基础知识

    C#网络编程基础知识一 1.IPAddress类 用于表示一个IP地址.IPAddress默认构造函数 public IPAddress(long address);一般不用 其中Parse()方法最 ...

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

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

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

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

  8. 循序渐进Socket网络编程(多客户端、信息共享、文件传输)

    循序渐进Socket网络编程(多客户端.信息共享.文件传输) 前言:在最近一个即将结束的项目中使用到了Socket编程,用于调用另一系统进行处理并返回数据.故把Socket的基础知识总结梳理一遍. 1 ...

  9. 服务器编程入门(4)Linux网络编程基础API

      问题聚焦:     这节介绍的不仅是网络编程的几个API     更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系.     这节主要介绍三个方面的内容:套接字( ...

随机推荐

  1. 服务没有mysql

    你安装了mysql没有,没有就先安装,安装好mysql以后,在bin目录下有个mysqld.exe,运行这个程序就可以添加mysql服务,但是运行这个程序需要使用参数,否则就会以命令行窗口形式启动MY ...

  2. Unix/Linux环境C编程入门教程(16) LinuxMint CCPP开发环境搭建

    1. Linux Mint由Linux Mint Team团队于2006年开始发行,是一份基于 这个时候linuxmint安装完成,C/C++开发环境也配置完成,希望大家认真实践!

  3. HDU 5809 Ants(KD树+并查集)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5809 [题目大意] 给出一些蚂蚁和他们的巢穴,一开始他们会在自己的巢穴(以二维坐标形式给出),之后 ...

  4. 简单实现计算Edit Distance算法

    最近因为工作需要,学习了NLP的相关知识,简单动手实现了一下计算Edit Distance的算法,就是计算一个字符串要变成另一个字符串需要的代价,这其中采用Levenshtein方式,即规定一个插入和 ...

  5. 像web一样使用python

    使用传统的web开发技术,也就是html+js,然后搭配一个后端语言,已经成为当今web开发的固定模式了,为此也形成了众多的toolkit,譬如ror,django,各种js图形库更是玲琅满目,从非常 ...

  6. 基于eclipse的mybatis映射代码自动生成的插件

    基于eclipse的mybatis映射代码自动生成的插件 分类: JAVA 数据库 工具相关2012-04-29 00:15 2157人阅读 评论(9) 收藏 举报 eclipsegeneratori ...

  7. URL中增加BASE64加密的字符串引起的问题(java.net.MalformedURLException:Illegal character in URL)

    序 昨天在做一个 Demo 的时候,因为是调用第三方的接口,採用的是 HTTP 的通信协议,依照文档上的说明,须要把參数进行加密后增加到 URL 中.可是,就是这个看似普普通通的操作,却让我着实费了非 ...

  8. 前端模块与CMS结合

    前端模块与CMS结合 在<FIS官方技术群>经常看到一些讨论,这次是 前端组件化与CMS的相关讨论,主要观点来自群里 漂流瓶(张云龙前辈). CMS是运营人员直接操作,我们往往需求各种各样 ...

  9. matlab GUI之自定义菜单小结

    自定义菜单 1.uimenu对象 h=uimenu('PropertyName','ProperValue') h=uimenu(parent,'PropertyName','ProperValue' ...

  10. SQL中存储过程和自定义函数的区别(转载)

    存储过程:     存储过程可以使得对数据库的管理.以及显示关于数据库及其用户信息的工作容易得多.存储过程是 SQL 语句和可选控制流语句的预编译集合,以一个名称存储并作为一个单元处理.存储过程存储在 ...