基于TCP(面向连接)的socket编程,分为客户端和服务器端。

客户端的流程如下:

(1)创建套接字(socket)

(2)向服务器发出连接请求(connect)

(3)和服务器端进行通信(send/recv)

(4)关闭套接字

服务器端的流程如下:

(1)创建套接字(socket)

(2)将套接字绑定到一个本地地址和端口上(bind)

(3)将套接字设为监听模式,准备接收客户端请求(listen)

(4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)

(5)用返回的套接字和客户端进行通信(send/recv)

(6)返回,等待另一个客户请求。

(7)关闭套接字。

下面通过一个具体例子讲解一下具体的过程和相关的函数。

客户端代码,运行于vs2008

// ClientTest.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h> #define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int ret;
SOCKET sClient; //连接套接字
struct sockaddr_in saServer; //服务器地址信息
char *ptr;
BOOL fSuccess = TRUE; //WinSock初始化
wVersionRequested = MAKEWORD(, ); //希望使用的WinSock DLL的版本
ret = WSAStartup(wVersionRequested, &wsaData); //加载套接字库
if(ret!=)
{
printf("WSAStartup() failed!\n");
//return 0;
}
//确认WinSock DLL支持版本2.2
if(LOBYTE(wsaData.wVersion)!= || HIBYTE(wsaData.wVersion)!=)
{
WSACleanup(); //释放为该程序分配的资源,终止对winsock动态库的使用
printf("Invalid WinSock version!\n");
//return 0;
} //创建Socket,使用TCP协议
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sClient == INVALID_SOCKET)
{
WSACleanup();
printf("socket() failed!\n");
//return 0;
} //构建服务器地址信息
saServer.sin_family = AF_INET; //地址家族
saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序
saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //连接服务器
ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer));
if (ret == SOCKET_ERROR)
{
printf("connect() failed!\n");
closesocket(sClient); //关闭套接字
WSACleanup();
//return 0;
} char sendMessage[]="ZhongXingPengYue";
ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), );
if (ret == SOCKET_ERROR)
{
printf("send() failed!\n");
}
else
printf("client info has been sent!");
char recvBuf[];
recv(sClient,recvBuf,,);
printf("%s\n",recvBuf);
closesocket(sClient); //关闭套接字
WSACleanup();
getchar();
//return 0;
} <span style="font-size:16px;"></span>
<span style="font-size:16px;"></span>   

第一步,加载套接字。使用WSAStartup 函数,如:ret = WSAStartup(wVersionRequested, &wsaData)。WSAStartup函数的原型为

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData) 

第一参数wVersionRequested,用来指定准备加载的winsock库的版本。利用MAKEWORD(x,y)宏来赋值。x是高位字节,表示副版本号;y是低位字节,表示主版本号。MAKEWORD(2, 2)表示版本号为2.2。

第二个参数是指向WSADATA结构的指针,是一个返回值,保存了库版本的有关信息。

第二步,创建套接字。使用socket函数,如:sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)。socket函数的原型为:

SOCKET socket(int af, int type, int protocol );  

第一个参数,指定地址族,对于TCP/IP协议的套接字,只能为AF_INET;

第二个参数,指定socket类型,SOCK_STREAM指产生流式套接字,SOCK_DGRAM指产生数据报套接字,TCP/IP协议使用SOCK_STREAM。

第三个参数,与特定的地址家族相关的协议,TCP协议一般为IPPROTO_TCP。也可以写0,那么系统会根据地址格式和套接字类别,自动选择一个适合的协议。

如果socket创建成功,则返回一个新的SOCKET数据类型的套接字描述符;若失败,则返回INVALID_SOCKET,由此可以判断是否创建成功。

第三步,连接服务器。使用connect函数,如:ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer))。connect函数函数原型为

int connect(SOCKET s, const struct sockaddr FAR* name, int namelen);  

第一个参数是将在上面建立连接的那个套接字的描述符,即之前创建socket的返回值sClient。

第二个参数是要连接的服务器端的地址信息。它是一个结构体类型struct sockaddr_in ,需要在调用connect函数之前构建服务器地址信息。sockaddr_in的定义如下:

struct sockaddr_in{  

short sin_family;  

unsigned short sin_port;  

struct in_addr sin_addr;  

char sin_zero[]  

};  

设置服务器端口时,用到htons函数,该函数把一个u_short类型的值从主机字节顺序转换为TCP/IP网络字节顺序,因为不同的计算机存放多字节的顺序不同(基于Intel CPU是高字节存放在低地址,低字节存放在高地址),所以网络中不同主机间通信时,要统一采用网络字节顺序。设置服务器IP地址时,使用到inet_addr函数,它是将点分十进制的IP地址的字符串转换成unsigned long型。inet_ntoa函数做相反的转换。

第三个参数是服务器端地址结构体的大小。

第四步,发送。使用send函数向服务器发送数据,如:ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0)。send函数的原型为

int send(SOCKET s, const char FAR* buf, int len, int flags);  

第一个参数,是一个与服务器已经建立连接的套接字。

第二个参数,指向包含要发送的数据的缓冲区的指针。

第三个参数,是所指向的缓冲区的长度。准确的说,应该是所要发送的数据的长度,因为不是缓冲区的所有数据都要同时发送。

第四个参数,它设定的值将影响函数的行为,一般将其设置为0即可。

如果发送失败,send会返回SOCKET_ERROR,由此可以判断发送是否成功。

第五步,接收。使用recv函数接收服务器发过来的数据,如recv(sClient,recvBuf,100,0)。recv函数的原型为

int recv(SOCKET s, const char FAR* buf, int len, int flags);  

recv函数的参数的含义和send函数参数含义差不多,只是第二个参数是指向用来保存接收数据的缓冲区的指针。recv函数的返回值应该是所接收的数据的长度,如果返回SOCKET_ERROR表示接收失败;返回0表示服务器端关闭连接。

第六步,关闭socket,释放资源。使用closesocket函数关闭套接字,如closesocket(sClient);使用WSACleanup函数释放为该程序分配的资源,终止对winsock动态库的使用,如WSACleanup();

服务器端代码,运行于vs2008

// ServerTest.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <stdio.h>
#include <winsock2.h> #define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int ret, nLeft, length;
SOCKET sListen, sServer; //侦听套接字,连接套接字
struct sockaddr_in saServer, saClient; //地址信息
char *ptr;//用于遍历信息的指针
//WinSock初始化
wVersionRequested=MAKEWORD(, ); //希望使用的WinSock DLL 的版本
ret=WSAStartup(wVersionRequested, &wsaData);
if(ret!=)
{
printf("WSAStartup() failed!\n");
//return 0;
}
//创建Socket,使用TCP协议
sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sListen == INVALID_SOCKET)
{
WSACleanup();
printf("socket() faild!\n");
//return 0;
}
//构建本地地址信息
saServer.sin_family = AF_INET; //地址家族
saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序
saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址 //绑定
ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));
if (ret == SOCKET_ERROR)
{
printf("bind() faild! code:%d\n", WSAGetLastError());
closesocket(sListen); //关闭套接字
WSACleanup();
//return 0;
} //侦听连接请求
ret = listen(sListen, );
if (ret == SOCKET_ERROR)
{
printf("listen() faild! code:%d\n", WSAGetLastError());
closesocket(sListen); //关闭套接字
//return 0;
} printf("Waiting for client connecting!\n");
printf("Tips: Ctrl+c to quit!\n");
//阻塞等待接受客户端连接
while()//循环监听客户端,永远不停止,所以,在本项目中,我们没有心跳包。
{
length = sizeof(saClient);
sServer = accept(sListen, (struct sockaddr *)&saClient, &length);
if (sServer == INVALID_SOCKET)
{
printf("accept() faild! code:%d\n", WSAGetLastError());
closesocket(sListen); //关闭套接字
WSACleanup();
return ;
} char sendMessage[]="hello client"; //发送信息给客户端
send(sServer,sendMessage,strlen(sendMessage)+,); char receiveMessage[];
nLeft = sizeof(receiveMessage);
ptr = (char *)&receiveMessage;
while(nLeft>)
{
//接收数据
ret = recv(sServer, ptr, , );
if (ret == SOCKET_ERROR)
{
printf("recv() failed!\n");
return ;
}
if (ret == ) //客户端已经关闭连接
{
printf("Client has closed the connection\n");
break;
}
nLeft -= ret;
ptr += ret;
}
printf("receive message:%s\n", receiveMessage);//打印我们接收到的消息。 }
// closesocket(sListen);
// closesocket(sServer);
// WSACleanup();
return ;
}

第一步,加载套接字库,和客户端得加载套接字一样。

第二步,创建监听套接字,sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);仍然使用的是socket函数。

第三步,绑定。使用bind函数,该函数的作用是将一个创建好的套接字绑定到本地的某个地址和端口上,该函数的原型为:

int bind(SOCKET s, const struct sockaddr FAR* name, int namelen);  

第一个参数,指定要绑定的套接字;

第二个参数,指定该套接字的地址信息,这里即服务器的地址信息,它仍是指向struct sockaddr_in类型的结构体的指针。这个结构体和客户端调用connect函数之前构建服务器地址信息的一样。其中INADDR_ANY 是指示任意地址,因为服务器含有可能多个网卡,可能有多个IP地址,这边指选择一个任意可用的地址。

第三个参数,地址的信息的长度。

第四步,监听连接。使用listen函数,该函数是将指定的套接字设置为监听模式,如ret = listen(sListen, 5)。函数原型为

int listen(SOCKET s, int backlog);  

第一个参数,是要设置为监听的套接字描述符。

第二个参数,是等待连接队列的最大的长度。注意了,设置这个值是为了设置等待连接队列的最大长度,而不是在一个端口上可以连接的最大数目。例如,设置为5,当有6个连接请求同时到达,前面5个连接请求会被放到等待请求连接队列中,然后服务器依次处理这些请求服务,但是第6个连接请求会被拒绝。

第五步,接受客户端的连接请求。使用accept函数接受客户端发送的连接请求,如sServer = accept(sListen, (struct sockaddr *)&saClient, &length);该函数的原型为

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

第一个参数,是一个已设为监听模式的socket的描述符。

第二个参数,是一个返回值,它指向一个struct sockaddr类型的结构体的变量,保存了发起连接的客户端得IP地址信息和端口信息。

第三个参数,也是一个返回值,指向整型的变量,保存了返回的地址信息的长度。

accept函数返回值是一个客户端和服务器连接的SOCKET类型的描述符,在服务器端标识着这个客户端。

第六、七步是发送和接收,分别使用send和recv函数,这里和客户端的一样,不再重复。

accept函数是放在一个死循环中的,一直监听客户的请求。当服务器关闭时要关闭所有的套接字,和释放资源。

【转/TCP协议编程】 基于TCP的Socket 编程的更多相关文章

  1. 网络编程(二)——TCP协议、基于tcp协议的套接字socket

    TCP协议与基于tcp协议的套接字socket 一.TCP协议(流式协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的 ...

  2. 网络编程(二)--TCP协议、基于tcp协议的套接字socket

    一.TCP协议(Transmission Control Protocol 传输控制协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会 ...

  3. Java 网络编程 -- 基于TCP 模拟多用户登录

    Java TCP的基本操作参考前一篇:Java 网络编程 – 基于TCP实现文件上传 实现多用户操作之前先实现以下单用户操作,假设目前有一个用户: 账号:zs 密码:123 服务端: public c ...

  4. 【TCP协议】(3)---TCP粘包黏包

    [TCP协议](3)---TCP粘包黏包 有关TCP协议之前写过两篇博客: 1.[TCP协议](1)---TCP协议详解 2.[TCP协议](2)---TCP三次握手和四次挥手 一.TCP粘包.拆包图 ...

  5. 《网络安全编程基础》之Socket编程

    <网络安全编程基础>之Socket编程 我的代码 server.c // server.cpp : Defines the entry point for the console appl ...

  6. 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

    Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...

  7. 网络编程: 基于TCP协议的socket, 实现一对一, 一对多通信

    TCP协议  面向连接 可靠的 面向字节流形式的 tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 TCP协议编码流程: 服务器端:                 客户端 实例化对 ...

  8. 基于C#的socket编程的TCP同步实现

    该博客源著地址https://www.cnblogs.com/sunev/archive/2012/08/05/2604189.html 一.摘要 总结一下基于C#的TCP传输协议的涉及到的常用方法及 ...

  9. 基于TCP协议的项目架构之Socket流传输的实现

    项目背景  某银行的影像平台由于使用时间长,服务器等配置原因,老影像系统满足不了现在日益增长的数据量的需求,所以急需要升级改造.传统的影像平台使用的是Oracle数据库和简单的架构来存储数据(视频.图 ...

  10. 基于C#的socket编程的TCP异步实现

    一.摘要 本篇博文阐述基于TCP通信协议的异步实现. 二.实验平台 Visual Studio 2010 三.异步通信实现原理及常用方法 3.1 建立连接 在同步模式中,在服务器上使用Accept方法 ...

随机推荐

  1. ibatis分页的两种方式

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 ...

  2. 【转】solr源码导入eclipse

     http://blog.csdn.net/vltic/article/details/19917377   (1)相应的开发环境准备          (1)jdk1.6+的安装和环境变量配置(命令 ...

  3. MySql中存储过程的理解

    到底什么是存储过程,又为什么需要使用存储过程? 存储过程简单来说,就是为以后的使用而保存的一条或多条MySQL语句的集合,可将其视为批文件,虽然它们的作用不仅限与批处理. 使用存储过程有3个主要的好处 ...

  4. webpack@3.6.0(1) -- 快速开始

    本篇内容 前言 配置入口和输出 热更新 loader配置 js代码压缩 html的打包与发布 前言 //全局安装 npm install -g webpack(3.6.0) npm init //安装 ...

  5. Codeforces Round #522 Div2C(思维)

    #include<bits/stdc++.h>using namespace std;int a[200007];int b[200007][7];int ans[200007];int ...

  6. 2017BAPC初赛A(思维,无序图,向量)

    #include<bits/stdc++.h>using namespace std;string goods,sister[100010];int x,m;unordered_map&l ...

  7. Java实例——基于jsoup的简单爬虫实现(从智联获取工作信息)

    这几天在学习Java解析xml,突然想到Dom能不能解析html,结果试了半天行不通,然后就去查了一些资料,发现很多人都在用Jsoup解析html文件,然后研究了一下,写了一个简单的实例,感觉还有很多 ...

  8. python文件名匹配

    待匹配文件:#FY3D_IPMNT_GBAL_L1_20180516_0003_030KM_MS.HDF 干扰文件:#FY3D_IPMNT_GBAL_L1_20180516_0003_030KM_MS ...

  9. UVa 10652(旋转、凸包、多边形面积)

    要点 凸包显然 长方形旋转较好的处理方式就是用中点的Vector加上旋转的Vector,然后每个点都扔到凸包里 多边形面积板子求凸包面积即可 #include <cstdio> #incl ...

  10. Linux--1 初识

    一.服务器核心知识 1.电脑和电脑的硬件组成 现在的人们几乎无时无刻不在使用着电脑!不管是桌上型电脑(桌机).笔记型电脑(笔电).平板电脑,还是智慧型手机等等,这些东西都算是电脑.虽然接触这么多,但是 ...