基于TCP 的Socket 编程
来源:互联网 发布:jsp项目绑定域名 编辑:程序博客网 时间:2024/05/18 03:33
之前老师布置了不少关于任务,都是有关程序之间的通讯的。最开始只会读写文件,自然就用的是文件进行数据交流。文件交换数据有个问题就是一个程序读或者写的时候,文件被占用,另一个程序如果继续进行读取文件,就会报错。所以我就降低读写的频率,但是还是免不了两个程序同时操作文件的时候。后来学了数据库,虽然感觉牛逼一点了,但是感觉其实还是一个意思。后来学长给了一个socket+protobuf的动态链接库,用起来确实要比文件好,传输速度快,频率还高。用文件传,频率低了两个程序之间就会有延迟,换了socket延迟问题就没有了。
这两天事情不是很多,想起把这个东西看懂。学长给的那个是用C#写的,而且加了protobuf,很多东西看不懂,而且老师要求写的东西是MFC,要在MFC用这一套东西的话,还是得弄懂它,重写一遍。所以先从简单的开始,先把这个socket看懂。找了不少资料,有个网站的东西还不错,(http://c.biancheng.net/cpp/socket/)看了之后,懂了那么些意思。
我这里的用是TCP传输协议。
socket的基本操作
1. socket()函数
在Windows下创建socket,用socket函数来创建,原型如下:
SOCKET socket(int af, int type, int protocol)
- af (Address Family),也就是 IP 地址类型。有AF_INET 和 AF_INET6。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址。
- type 为数据传输方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM。
- protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。简单理解就是TCP会保证数据传输的到达,UDP就是发出去就不管了。
一般情况下,有了af,type两个参数就可以确定创建套接字了。所以下面的代码,第三个参数就写成0。
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
2. bind()函数 connet()函数
1). bind()函数
创建套接字后服务器需要bind函数将套接字与特定的IP地址和端口绑定起来,只有这样,流经该IP地址和端口的数据才能交给套接字处理。原型如下:
int bind(SOCKET sock, const struct sockaddr *addr, int addrlen);
- sock指前面创建的套接字
- addr 为 sockaddr 结构体变量的指针。我后面的代码写的却是sockaddr_in类型,然后又强制转换成sockaddr类型。这里为什么这么麻烦,可以认为,sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体。
- addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。
sockaddr_in sockAddr;// memset(&sockAddr, 0, sizeof(sockAddr));//每个字节用0填充 sockAddr.sin_family = PF_INET;//设置IP为IPv4类型 sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 就写127.0.0.1 就可以了,指本地机,一般用来测试使用。 sockAddr.sin_port = htons(8888);//设置一个端口号 最好在1024~65536 之间分配端口号。 //sockaddr_in 结构体变量创建好后,要强制转换成SOCKADDR型,就可以将其与前面的套接字绑定起来。 if (-1 == bind(serSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR))) { //这里这样写如果程序出错,可以得到错误编码。我当时就是IP地址的点写成了逗号,找了好久的错(哈哈) DWORD err = GetLastError(); printf("bind error:%d\n", err); return 0; }
2). connect()函数
客户端在创建套接字之后就可以用connect()函数来建立连接了,原型为:
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);
参数的说明和 bind() 相同
3. listen()函数
作为一个服务器,在调用socket()、bind()之后就会通过 listen() 函数可以让套接字进入被动监听状态。
原型如下:
int listen(SOCKET sock, int backlog);
- sock 为需要进入监听状态的套接字。
- backlog 为请求队列的最大长度。能存放多少个客户端请求。
4. accept() 函数
作为服务器套接字执行socket() bind() listen()后就处于于监听状态时,可以通过 accept() 函数来接收客户端请求。它的原型为:
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
- 它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
- accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字,大家注意区分。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。
- accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来,就是客户端执行connect()。
//接收客户端请求 SOCKADDR clntAddr;//客户端地址 int nSize = sizeof(SOCKADDR); SOCKET clntSock = accept(serSock, (SOCKADDR*)&clntAddr, &nSize);//返回后面用于与客户端通信用的新套接字。
5. 数据的接收和发送
服务器与客户端的套接字建立连接后,可以调用网络I/O进行读写操作了。
我这里就只用了send() recv() 原型如下:
int send(SOCKET sock, const char *buf, int len, int flags);int recv(SOCKET sock, char *buf, int len, int flags);
- sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数,flags 为发送数据时的选项,设置为0就可以了。
6. 关闭连接
要关闭服务器与客户端的连接。直接调用closesocket()。参数就是前面创建的套接字。
两个程序的源码
先执行服务器程序,然后执行客户端程序
- 服务器端
执行的函数依次是socket() bind() listen() accpet() send() closesocket()
#include "stdio.h"#include "winsock2.h"#pragma comment (lib, "ws2_32.lib")//windows下这些函数写在了winsock2里,所以这里就照敲int main(){ //初始化DLL WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET serSock = socket(PF_INET, SOCK_STREAM, 0); if (serSock == INVALID_SOCKET) { printf("socket error!"); return 0; } //绑定套接字 sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr));//每个字节用0填充 sockAddr.sin_family = PF_INET;//IPv4 sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 具体IP地址 sockAddr.sin_port = htons(8888);//端口 if (-1 == bind(serSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR))) { DWORD err = GetLastError(); printf("bind error:%d\n", err); return 0; } //监听状态 listen(serSock, 20); //等待接收客户端请求 SOCKADDR clntAddr; int nSize = sizeof(SOCKADDR); SOCKET clntSock = accept(serSock, (SOCKADDR*)&clntAddr, &nSize); //向客户端发送数据 char buffer[100] = { "Hello World!" }; send(clntSock, buffer, strlen(buffer) + sizeof(char), NULL);//发送数据 //关闭套接字 closesocket(serSock); //终止DLL 使用 WSACleanup(); return 0;}
- 客户端
执行的函数依次是socket() connect() recv() closesocket()
#include "stdio.h"#include "winsock2.h"#pragma comment (lib, "ws2_32.lib")int main(){ //初始化dll WSADATA wsaData; WORD sockVersion = MAKEWORD(2,2); if (WSAStartup(sockVersion,&wsaData) != 0) { return 0; } //创建套接字 SOCKET clnSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (clnSocket == INVALID_SOCKET) { printf("socket error!"); return 0; } //绑定套接字 bind sockaddr_in sockAddr; sockAddr.sin_family = PF_INET;//IPv4 sockAddr.sin_port = htons(8888);//端口 sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//具体IP地址 //connet 连接服务器 connect(clnSocket, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //接受服务器传回的数据 char szBuffer[MAXBYTE] = { 0 }; recv(clnSocket, szBuffer, MAXBYTE, NULL); printf("Message form server :%s\n", szBuffer); closesocket(clnSocket); WSACleanup(); return 0;}
- 基于TCP的socket编程
- 基于TCP的socket编程
- 基于TCP的socket编程
- 基于TCP的socket编程
- 基于TCP的socket编程
- 基于TCP的Socket 编程
- 基于TCP的socket编程
- 基于TCP的Socket编程
- 基于TCP的Socket 编程
- 基于TCP的Socket编程
- 基于TCP的Socket编程
- 基于tcp的socket编程。
- 基于TCP的Socket 编程
- 基于TCP的Socket 编程
- 基于TCP的socket编程
- 基于TCP的socket编程
- 基于TCP的socket编程
- 基于TCP 的Socket 编程
- u8sdk走过的坑-ExtMXSerializer.writeAttributeValue
- HDU 1914 The Stable Marriage Problem
- c++排序算法之插入法
- const int*和int const*的区别
- Servlet学习
- 基于TCP 的Socket 编程
- Jave EE 写第一个servlet
- POJ 2492 A Bug's Life(并查集)
- webrtc——web与android,android间通信
- 使用jquery制作日历(生日)选择器
- 516. 「LibreOJ β Round #2」DP 一般看规律 [set/SPLAY] 【STL/数据结构】
- [杜教筛+莫比乌斯反演] HDU5608: function
- (leetcode)Path Sum III 挺有意思的一题
- git学习笔记