zoukankan      html  css  js  c++  java
  • 基于TCP的socket通信过程及例子

    Socket也叫套接字,用来实现网络通讯,通过调用系统提供的API,可以和远程的机子传输数据。Socket有很多种协议,而这篇文章主要讨论TCP部分的内容,也就是说后面说的内容主要是指TCP Socket。

    Socket 的一般调用过程:

    服务端:socket(), bind(),listen(),accept(),send(),recv(),close()

    客户端:socket(),connect(),send(),recv(),close()

    阻塞socket(同步socket)

    进程或线程执行到某些socket函数时必须等待该socket事件的发生,如果该事件没有发生,socket函数不能立即返回,进程或线程被阻塞。

    特点:使用简单,适合一对一的应答场合,在服务端很少使用,或配合多线程使用

    函数 返回值说明 阻塞情况
    accept() 返回新的连接socket句柄。 缓冲区队列没有新的等待连接
    connect() 返回-1说明连接失败,其他正常。 连接过程阻塞。
    recv() 返回值小于1代表接收失败,其他代表接收数据的长度。 发送缓冲区有数据等待发送完成,或接收缓冲区没数据时阻塞。
    send() 返回-1代表发送失败,其他为发送数据的长度 发送缓冲区没有足够空间保存此次发送数据时阻塞


    非阻塞socket(异步socket)

    进程或线程执行socket函数时不必非要等待该socket事件的发生,一旦执行立即返回。根据返回值的不同可以判断函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程可以不被阻塞,继续执行。

    特点:函数执行立即返回,不会阻断进程,性能比阻塞高,适合在主线程直接调用,不会造成主线程卡顿现象

    因为socket默认是阻塞的,所以要设置非阻塞模式:

    #ifdef WIN32 
    	DWORD nMode = 1; 
    	ioctlsocket(m_sock, FIONBIO, &nMode);
    #else
    	int r = fcntl(fd, F_GETFL, 0));
    	fcntl(fd, F_SETFL, r|O_NONBLOCK);
    #endif

    TCP与UDP 的区别


    协议说明 socket创建
    TCP 传输控制协议,可靠的连接服务。双方先建立连接再传输数据。提供超时重发,数据检验,流量控制等机制,保证数据发送无误。 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
    UDP 用户数据报协议,不可靠的连接服务。没有建立连接就可以发送数据,没有超时重发机制,传输速度很快。 socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)

    Socket例子

    下面以一个简单例子来说明服务端与客户端的交互过程

    服务端 server.cpp

    #include <stdio.h>
    #include <WinSock2.h>
    #pragma comment(lib, "ws2_32.lib")
    
    #define PORT 8080
    
    int main()
    {
    	//初始化winsock服务
    	WSADATA wsaData;
    	WSAStartup(MAKEWORD(2,2), &wsaData);
    
    	//创建socket
    	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
    	struct sockaddr_in svraddr;
    	svraddr.sin_family = AF_INET;
    	svraddr.sin_port = htons(PORT);
    	svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    	//绑定socket
    	bind(sock, (struct sockaddr*)&svraddr, sizeof(svraddr));
    
    	//监听socket
    	listen(sock, 5);
    
    	while(1)
    	{
    		struct sockaddr_in addr;
    		int len = sizeof(SOCKADDR);
    
    		char buf[1024] = {0};
    
    		//接受客户端连接
    		SOCKET client = accept(sock, (struct sockaddr*)&addr, &len);
    
    		char* ip = inet_ntoa(addr.sin_addr);
    		printf("accept client: %s
    ", ip);
    
    		//接收客户端数据
    		if(recv(client, buf, 1024, 0) >0)
    		{
    			printf("recv client: %s
    ", buf);
    
    			//向客户端发送数据
    			send(client, "hello, client", strlen("hello, client"), 0);
    		}
    		closesocket(client);
    	}
    
    	//关闭socket
    	closesocket(sock);
    
    	//关闭winsock服务
    	WSACleanup();
    
    	return 0;
    }

    客户端 client.cpp

    #include <stdio.h>
    #include <WinSock2.h>
    #pragma comment(lib, "ws2_32.lib")
    
    #define REMOTE_IP "127.0.0.1"
    #define REMOTE_PORT 8080
    
    int main()
    {
    	//初始化winsock服务
    	WSADATA wsaData;
    	WSAStartup(MAKEWORD(2,2), &wsaData);
    
    	//创建socket
    	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
    	struct sockaddr_in svraddr;
    	svraddr.sin_family = AF_INET;
    	svraddr.sin_port = htons(REMOTE_PORT);
    	svraddr.sin_addr.s_addr = inet_addr(REMOTE_IP);
    
    	//连接socket
    	if( connect(sock, (struct sockaddr*)&svraddr, sizeof(svraddr)) != -1)
    	{
    		//发送数据给服务端
    		if(send(sock, "hello, server", strlen("hello, server"), 0) != -1)
    		{
    			//接收服务端数据
    			char buf[1024] = {0};
    			if(recv(sock, buf, 1024, 0) >0)
    			{
    				printf("recv server: %s
    ", buf);
    			}
    			
    		}
    	}
    	else
    	{
    		printf("can not connect server
    ");
    	}
    
    	//关闭socket
    	closesocket(sock);
    
    	//关闭winsock服务
    	WSACleanup();
    	getchar();
    	return 0;
    }


  • 相关阅读:
    理解SetCapture、ReleaseCapture、GetCapture(控制了消息发往哪个窗口,是理解消息的关键)
    Javascript 的addEventListener()及attachEvent()区别分析
    鼠标拖拽
    鼠标右键菜单
    keydown
    一串跟随鼠标的DIV
    event对象和事件冒泡
    发表说说
    文档流
    CSS3 @keyframes 规则
  • 原文地址:https://www.cnblogs.com/fuhaots2009/p/3455311.html
Copyright © 2011-2022 走看看