为您推荐:
精华内容
最热下载
问答
  • 5星
    5KB lj_70596 2021-01-18 19:37:52
  • 5星
    218.92MB xiaolong1126626497 2021-08-03 15:21:12
  • 5星
    82.7MB Roger_717 2021-07-29 14:56:18
  • 5星
    108.34MB guoruibin123 2021-04-27 06:03:44
  • 3星
    4KB qq_18108083 2018-11-21 15:02:59
  • 前段时间做项目用到了网络通讯,自己造了TCP通讯的轮子,能同时在window和linux下运行,并且封装成,方便以后使用,考虑到自己也一直在互联网上获取资源,现在也把我做的轮子也分享给大家,欢迎参考~ 完整代码附...

    前段时间做项目用到了网络通讯,自己造了TCP通讯的轮子,能同时在window和linux下运行,并且封装成类,方便以后使用,考虑到自己也一直在互联网上获取资源,现在也把我做的轮子也分享给大家,欢迎参考~

    完整代码附在下面,如果大家不想复制,可直接从这下载:https://download.csdn.net/download/qq_18108083/10798550

    (1) XTcp.h

    #ifndef XTCP_H
    #define XTCP_H
    
    #include <string>
    
    class XTcp
    {
    public:
    	int CreateSocket();				 //创建套接字
    	bool Bind(unsigned short port);  //绑定端口号
    	bool Listen(unsigned short num); //监听端口号
    	bool SetBlock(bool isblock);  //设置阻塞模式  (希望只有在connect的时候是非阻塞的,而接收数据时候是阻塞的)
    	bool Connect(const char *ip = "192.168.0.123", unsigned short port = 8000, int sec = 3);
    	bool Connect(int sec = 3);
    	XTcp Accept();                   //返回XTcp对象,接收连接
    	void Close();							//关闭连接
    	int Recv(char *buf, int size);          //接收数据
    	int Send(const char *buf, int size);	//发送数据
    	int SetRecvTimeout(int sec = 1);			 //设置udp接收超时
    	int SetSendTimeout(int sec = 1);		     //设置udp发送超时
    
    
    	char clientip[16];						//存放接收到的client ip
    	unsigned short clientport = 0;			//存放接收到的client port
    
    	XTcp(unsigned short port = 8000);
        XTcp(char *ip,unsigned short port);
    	virtual ~XTcp();
    
    
    private:
    	char tcp_serverip[16] = "";   //tcp_serverip 服务端ip
    	int tsock = 0;							//tcp客户端的socket,create自动生成
    	unsigned short tport = 0;				//接收main函数中的参数  要传入Connect
    };
    
    #endif

    (2) XTcp.cpp

    #include "XTcp.h"
    #include <iostream>
    #include "string.h"
    
    #ifdef WIN32
    #include <Windows.h>
    #define socklen_t int
    #else
    #include <arpa/inet.h>
    #define closesocket close    //宏定义替换函数
    #include <unistd.h>
    #include <fcntl.h>
    #include <pthread.h>
    
    #define strcpy_s strcpy
    #endif
    
    XTcp::XTcp(unsigned short port)
    {
    	//初始化动态链接库
    	//引用lib库
    #ifdef WIN32  //linux下不用初始化网络库
    	static bool first = true;
    	if (first)
    	{
    		first = false;						 //只在首次进入时初始化网络库
    		WSADATA ws;					         //加载Socket库   项目属性-链接器-输入加上 ws2_32.lib
    		WSAStartup(MAKEWORD(2, 2), &ws);     //动态库引用加1    
    	}
    
    #endif
    	tport = port;  //接收main函数的参数   //
    }
    
    XTcp::XTcp(char *ip,unsigned short port)
    {
    	//初始化动态链接库
    	//引用lib库
    #ifdef WIN32  //linux下不用初始化网络库
    	static bool first = true;
    	if (first)
    	{
    		first = false;						 //只在首次进入时初始化网络库
    		WSADATA ws;					         //加载Socket库   项目属性-链接器-输入加上 ws2_32.lib
    		WSAStartup(MAKEWORD(2, 2), &ws);     //动态库引用加1    
    	}
    
    #endif
    	strcpy(tcp_serverip,ip);   			//tcp_serverip 服务端ip
    	tport = port;  //接收main函数的参数   //
    }
    
    
    
    XTcp::~XTcp()
    {
    	delete this;
    }
    
    
    int XTcp::CreateSocket()				 //创建套接字
    {
    	//创建socket (TCP/IP协议 TCP)
    	tsock = socket(AF_INET, SOCK_STREAM, 0);    //直接创建socket返回给XTcp的成员函数
    	if (tsock == -1)
    	{
    		printf("create tcp socket failed.\n");
    		return -1;
    	}
    	else
    	{
    		printf("create tcp socket successed.\n");
    		return tsock;
    	}
    }
    
    bool XTcp::Bind(unsigned short port)  //绑定并监听端口号(服务端用)
    {
    	sockaddr_in saddr;              //数据结构
    	saddr.sin_family = AF_INET;     //协议
    	saddr.sin_port = htons(port);   //端口,主机字节序(小端方式)转换成网络字节序(大端方式)             
    	saddr.sin_addr.s_addr = htonl(INADDR_ANY);   //绑定IP到广播地址INADDR_ANY 0.0.0.0  为了兼容linux   
    
    	if (bind(tsock, (sockaddr*)&saddr, sizeof(saddr)) != 0)  
    	{
    		printf("tcp bind port %d failed.\n", port);
    		return false;
    	}
    	printf("tcp bind port %d success.\n", port);
    	return true;
    }
    
    bool XTcp::Listen(unsigned short num) //监听端口号
    {
    	int re = listen(tsock, 10);   //套接字,最大请求队列的长度   进入阻塞状态
    	if(!re)
    	{
    		printf("tcp socket listen start.\n");
    		return false;
    	}
    	else
    	{
    		printf("tcp socket listen failed.\n");
    		return true;
    	}
    }
    
    bool XTcp::SetBlock(bool isblock)  //设置阻塞模式  (希望只有在connect的时候是非阻塞的,而接收数据时候是阻塞的)
    {
    	if (tsock <= 0)
    	{		
    		printf("set tcp socket block failed.\n");
    		return false;
    	}
    #ifdef WIN32
    	unsigned long ul = 0;
    	if (!isblock) ul = 1;
    	ioctlsocket(tsock, FIONBIO, &ul);    //设置socket的模式(0 阻塞模式,1 非阻塞模式<connect就立即返回>)
    #else
    	int flags = fcntl(tsock, F_GETFL, 0);  //获取socket的属性
    	if (flags < 0)return false; //获取属性出错
    	if (isblock)
    	{
    		flags = flags&~O_NONBLOCK;  //把非阻塞这位设为0
    	}
    	else
    	{
    		flags = flags | O_NONBLOCK; //把非阻塞这位设为1
    	}
    	if (fcntl(tsock, F_SETFL, flags))return false;  //把标准位设回去
    #endif
    
    	if (isblock==0)
    		printf("set tcp socket not block success.\n");
    	if (isblock==1)
    		printf("set tcp socket block success.\n");
    
    	return true;
    }
    
    bool XTcp::Connect(const char *ip, unsigned short port , int sec)
    {
    	if (tsock <= 0)	return false;
    
    	sockaddr_in saddr;   //设置连接对象的结构体
    	saddr.sin_family = AF_INET;
    	saddr.sin_port = htons(port);
    	saddr.sin_addr.s_addr = inet_addr(ip);  //字符串转整型
    
    	SetBlock(false);    //将socket改成非阻塞模式,此时它会立即返回  所以通过fd_set
    	fd_set rfds, wfds;	    //文件句柄数组,在这个数组中,存放当前每个文件句柄的状态
    
    	if (connect(tsock, (sockaddr*)&saddr, sizeof(saddr)) != 0)   //此时connect马上返回,状态为未成功连接
    	{
    
    	    FD_ZERO(&rfds);  //首先把文件句柄的数组置空
    	    FD_ZERO(&wfds);  
    	    FD_SET(tsock, &rfds);   //把sock的网络句柄加入到该句柄数组中
    	    FD_SET(tsock, &wfds); 
    
    
    		timeval tm;  //超时参数的结构体
    		tm.tv_sec = sec;
    		tm.tv_usec = 0;
    
    		int selres = select(tsock + 1, &rfds, &wfds, NULL, &tm);   //(阻塞函数)(监听的文件句柄的最大值加1,可读序列文件列表,可写的序列文件列表,错误处理,超时)使用select监听文件序列set是否有可读可写,这里监听set数组(里面只有sock),只要其中的句柄有一个变得可写(在这里是sock连接成功了以后就会变得可写,就返回),就返回
    		switch (selres)
    		{
    
    			case -1:
                        printf("select error\n");  
    					return false;
                case 0:  
                   		printf("select time out\n");  
    					return false;
    			default:
    					if (FD_ISSET(tsock, &rfds) || FD_ISSET(tsock, &wfds)) 
    					{
    							connect(tsock, (sockaddr*)&saddr, sizeof(saddr));    //再次连接一次进行确认
    							int err = errno;  
    							if  (err == EISCONN||err == EINPROGRESS)     //已经连接到该套接字 或 套接字为非阻塞套接字,且连接请求没有立即完成
    							{  
    					    		printf("connect %s : %d finished(success).\n",ip,port);  
    							    SetBlock(true);   //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
    							    return true;
    							}  
    							else  
    							{  
    							    printf("connect %s : %d finished(failed). errno = %d\n",ip,port,errno);  
    							   // printf("FD_ISSET(sock_fd, &rfds): %d\n FD_ISSET(sock_fd, &wfds): %d\n", FD_ISSET(sock_fd, &rfds) , FD_ISSET(sock_fd, &wfds));  
    							    return false;
    							}  
    					}
    					else
    						{
    							    printf("connect %s : %d finished(failed).",ip,port);  
    							    return false;
    						}
    		}
    
    	}
    	else  //连接正常
    	{
    		SetBlock(true);   //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
    		printf("connect %s : %d finished(success).\n",ip,port);  
    		return true;
    		}
    }
    
    bool XTcp::Connect(int sec)
    {
    	if (tsock <= 0)	return false;
    
    	sockaddr_in saddr;   //设置连接对象的结构体
    	saddr.sin_family = AF_INET;
    	saddr.sin_port = htons(tport);
    	saddr.sin_addr.s_addr = inet_addr(tcp_serverip);  //字符串转整型
    
    	SetBlock(false);    //将socket改成非阻塞模式,此时它会立即返回  所以通过fd_set
    	fd_set rfds, wfds;	    //文件句柄数组,在这个数组中,存放当前每个文件句柄的状态
    
    	if (connect(tsock, (sockaddr*)&saddr, sizeof(saddr)) != 0)   //此时connect马上返回,状态为未成功连接
    	{
    
    	    FD_ZERO(&rfds);  //首先把文件句柄的数组置空
    	    FD_ZERO(&wfds);  
    	    FD_SET(tsock, &rfds);   //把sock的网络句柄加入到该句柄数组中
    	    FD_SET(tsock, &wfds); 
    
    
    		timeval tm;  //超时参数的结构体
    		tm.tv_sec = sec;
    		tm.tv_usec = 0;
    
    		int selres = select(tsock + 1, &rfds, &wfds, NULL, &tm);   //(阻塞函数)(监听的文件句柄的最大值加1,可读序列文件列表,可写的序列文件列表,错误处理,超时)使用select监听文件序列set是否有可读可写,这里监听set数组(里面只有sock),只要其中的句柄有一个变得可写(在这里是sock连接成功了以后就会变得可写,就返回),就返回
    		switch (selres)
    		{
    
    			case -1:
                        printf("select error\n");  
    					return false;
                case 0:  
                   		printf("select time out\n");  
    					return false;
    			default:
    					if (FD_ISSET(tsock, &rfds) || FD_ISSET(tsock, &wfds)) 
    					{
    							connect(tsock, (sockaddr*)&saddr, sizeof(saddr));    //再次连接一次进行确认
    							int err = errno;  
    							if  (err == EISCONN||err == EINPROGRESS)     //已经连接到该套接字 或 套接字为非阻塞套接字,且连接请求没有立即完成
    							{  
    							    printf("connect %s : %d finished(success).\n",tcp_serverip,tport);  
    							    SetBlock(true);   //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
    							    return true;
    							}  
    							else  
    							{  
    							    printf("connect %s : %d finished(failed). errno = %d\n",tcp_serverip,tport,errno);  
    							   // printf("FD_ISSET(sock_fd, &rfds): %d\n FD_ISSET(sock_fd, &wfds): %d\n", FD_ISSET(sock_fd, &rfds) , FD_ISSET(sock_fd, &wfds));  
    							    return false;
    							}  
    					}
    					else
    						{
    							    printf("connect %s : %d finished(failed).",tcp_serverip,tport);  
    							    return false;
    						}
    		}
    
    	}
    	else  //连接正常
    	{
    		printf("connect %s : %d finished(success).\n",tcp_serverip,tport);  
    		SetBlock(true);   //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
    		return true;
    	}
    }
    
    
    XTcp XTcp::Accept()                   //返回XTcp对象,接收连接
    {
    	XTcp tcp;     //先定义一个XTcp对象,一会返回它
    
    	sockaddr_in caddr;
    	socklen_t len = sizeof(caddr);
    
    	tcp.tsock = accept(tsock, (sockaddr*)&caddr, &len);  //(阻塞)接收连接  ,会创建一个新的socket,一般扔到一个单独线程与这个客户端进行单独通信,之前的sock只用来建立连接
    	if (tcp.tsock <= 0)	return tcp;   //出错
    	printf("accept client %d\n", tcp.tsock);
    	char *ip = inet_ntoa(caddr.sin_addr);				 //解析出IP地址  ,转换到字符串
    
    	strcpy_s(tcp.clientip, ip);
    	tcp.clientport = ntohs(caddr.sin_port);		 //解析出端口,转换成主机字节序
    	printf("client ip is %s,port is %d\n", tcp.clientip, tcp.clientport);  //打印ip和端口
    	return tcp;
    
    }
    
    void XTcp::Close()                    //关闭连接
    {
    	if (tsock <= 0) return;  //socket出错
    	closesocket(tsock);		//已宏定义
    	tsock = 0;
    }
    
    int XTcp::Recv(char *buf, int size)                      //接收数据
    {
    	return recv(tsock, buf, size, 0);
    }
    
    int XTcp::Send(const char *buf, int size)					     //发送数据
    {
    	int sendedSize = 0;   //已发送成功的长度
    	while (sendedSize != size)   //若没发送完成,则从断点开始继续发送 直到完成
    	{
    		int len = send(tsock, buf + sendedSize, size - sendedSize, 0);
    		if (len <= 0)break;
    		sendedSize += len;
    	}
    	return sendedSize;
    }
    
    int XTcp::SetRecvTimeout(int sec)   //设置tcp接收超时
    {
    #ifdef WIN32
    	int tcp_rev_time = sec * 1000;
    	if (setsockopt(tsock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tcp_rev_time, sizeof(int))<0)
    	{
    		printf("set tcp receive failed.\n");
    		return -1;
    	}
    	printf("set tcp recv timeout success. %d seconds.\n", sec);
    	return 0;
    #else
    	struct timeval tcp_rev_time;
    	tcp_rev_time.tv_sec = sec;
    	tcp_rev_time.tv_usec = 0;
    	if (setsockopt(tsock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tcp_rev_time, sizeof(tcp_rev_time))<0)
    	{
    		printf("set tcp receive failed.\n");
    		return -1;
    	}
    	printf("set tcp recv timeout success. %d seconds.\n", sec);
    	return 0;
    #endif
    }
    
    int XTcp::SetSendTimeout(int sec)   //设置tcp发送超时
    {
    #ifdef WIN32
    	int tcp_send_time = sec;
    	if (setsockopt(tsock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tcp_send_time, sizeof(int))<0)
    	{
    		printf("set tcp send failed.\n");
    		return -1;
    	}
    	return 0;
    	printf("set tcp recv timeout success. %d seconds.\n", sec);
    #else
    	struct timeval tcp_send_time;
    	tcp_send_time.tv_sec = sec;
    	tcp_send_time.tv_usec = 0;
    	if (setsockopt(tsock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tcp_send_time, sizeof(tcp_send_time))<0)
    	{
    		printf("set tcp send failed.\n");
    		return -1;
    	}
    	printf("set tcp recv timeout success. %d seconds.\n", sec);
    	return 0;
    #endif
    }

     

    展开全文
    qq_18108083 2018-11-21 15:05:09
  • 4星
    3KB htttw 2012-04-28 16:38:02
  • 2KB zkfopen 2018-08-08 13:46:08
  • 4星
    22.57MB weixin_43907849 2018-12-21 15:10:48
  • 文章目录TCP协议特性TCP报头解析基于TCP协议的应用层通信TCP服务端TCP客户端 TCP协议特性 TCP报头解析 作为传输层协议,源端口和目的端口用来唯一标识源进程和目的进程。 序号和确认序号作为本报文第一个字段的...

    TCP通信过程

    在这里插入图片描述

    • 服务器在明处,客户端在暗处。 对于客户端来说,客户端有服务器的IP地址和端口号,而对于服务器来讲,服务器不知道客户端在哪里,只有当客户端主动发送请求之后才能知道客户端的IP和端口。
    • TCP请求由客户端发起。 当客户端需要服务器的服务时候,客户端要首先建立连接。在建立连接之前要创建套接字socket。
    • 客户端不建议绑定地址,因为对于客户端,我并不关心我的数据使用哪个地址哪个端口发送。而且我也不能实时了解操作系统的哪个端口处于闲置状态。所以我不绑定地址和端口,操作系统可以给我分配端口,并添加IP地址。
    • 服务器必须绑定地址这些socket都是由服务器自己创建并绑定地址的,而这个IP地址和端口都是唯一确定的。(服务器地址如果修改,就需要对原地址进行重定向,否则,客户端会找不到服务器。)所以在socket创建好之后,就必须绑定地址。
    • 客户端连接之前,服务器一直有socket在等待,服务端的任务就是提供服务,按理说,服务器应该实时具有处于LISTEN状态的socket,以保证客户端的连接请求都能被处理。

    1、创建套接字

    建立与网卡的关联,协议版本的选择,传输层协议的选择

    int socket(int domain, int type, int protocol);
    
    • domain: 地址域
      AF_INET(协议类型)一般使用ipv4协议
    • type: 套接字类型
      SOCK_STREAM 流式套接字
      SOCK_DGRAM 数据报套接字
    • protocol(协议类型):0-默认;流式套接字默认TCP协议IPPROTO_TCP,数据报套接字默认UDP协议IPPROTO_UDP
    • 返回值:套接字描述符,失败:-1
    On  success, a file descriptor for the new socket is returned.  On error, -1 is
           returned, and errno is set appropriately.
    

    2、为套接字绑定地址信息(ip、port)

    bind函数既可以绑定IPV4版本协议,也可以绑定IPV6版本,两个版本的IP头结点的大小不同,定义sockaddr为了寻求接口的统一,都要使用struct sockaddr

    struct sockaddr_in		//IPV4
    struct sockaddr_in6		//IPV6
    

    为socket绑定地址信息,确定socket能够操作缓冲区中的哪些数据

    int bind(int sockfd, struct sockaddr *addr,socklen_t addrlen);
    
    • sockfd: 套接字描述符
    • addr: 要绑定的地址信息
    • addrlen:地址信息的长度

    3、监听 对于服务器而言

    int listen(int sockfd, int backlog);
    
    • sockfd:sockfd描述符,backlog:最大同时并发连接数(已完成连接队列中允许存放的socket个数)
    • 半连接的socket:当客户端发送来SYN连接请求时,服务器知道了客户端想要建立连接。可是客户端并不知道自己的连接请求是否已经到达服务器。这个状态下的连接处于半连接,服务器会将自己的这个处于半连接的请求放入一个半连接请求的队列来维护。同时发送给客户端ACK+SYN,告诉客户端“你发送的连接请求我已经收到了,我也要建立连接了。”。
    • 已经完成连接的socket:当客户端收到服务器发送的ACK和SYN之后,客户端会给服务一个应答报文ACK,服务器接受到这个报文之后,服务器关于这个连接的socket会放入已完成连接队列进行维护。这个时候,服务器才会为这个连接开辟一个数数据结构,开辟更多资源,以满足这个连接的服务。
    • 无论是已经完成连接接的socket还是处于半连接的socket,操作系统都要有一个队列去维护。
      在这里插入图片描述

    4、连接请求 对于客户端而言

     int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    
    • sockfd:客户端的socket文件描述符
    • addr:要连接的服务端地址
    • addrlen:要连接的服务端的socket结构体的长度,单位是字节
    • 返回值:成功:0,失败:-1

    5、接受连接请求 对于服务器而言

    accept函数是一个阻塞型的函数,连接成功队列中如果没有新的连接到来,那么就会一直阻塞直到有新的客户端连接到来

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    • sockfd:这里的sockfd是服务器新创建的socket的描述符
    • addr:新建立连接的客户端地址信息
    • addrlen:客户端的socket结构体的长度,单位是字节
    • 返回值
      成功:返回新的socket描述符 失败返回:-1
    On success, these system calls return a nonnegative integer that is a  descriptor
     for the accepted socket.  On error, -1 is returned, and errno is set appropriately.
    

    当服务端成功接受连接请求之后,会为新连接重新创建一个socket用于专门和这个连接的通信,而这个重新创建的socket是由accept这个函数完成的。 而原来socket函数返回的socket只是用于接受连接,并不用来进行通信。

    在这里插入图片描述

    6、接收/发送数据

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    
    • sockfd:连接成功的socket描述符
    • 返回值
      错误:-1,连接关闭:0,成功:实际接受长度>0
    • flags:默认0-阻塞式接收
    • buf: 用于指定接收数据的存放位置
    • len: 用于指定希望接收的数据长度

    7、关闭套接字

    int close(int fd);
    

    我们说socket函数的返回值是socket描述符,实际也是文件描述符。因为系统的文件描述符有限,我们不再使用这个文件时候,就应该关闭这个描述符,否则可能造成文件描述符泄漏问题。

    基于TCP协议的应用层聊天程序

    TCP服务端

    创建套接字——>绑定地址——>监听——>accept——>recv——>send——>close。

    //tcp服务端的代码
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<errno.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    
    int main()
    {
        //1、创建socket套接字
        int sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(sockfd<0){
            perror("socket error");
            return -1;
        }
        struct sockaddr_in ser;
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi("9000"));
        ser.sin_addr.s_addr=inet_addr("192.168.117.130");
    
        //2、绑定地址
        socklen_t len=sizeof(struct sockaddr_in);
        if(bind(sockfd,(struct sockaddr*)&ser,len)<0)
        {
            perror("bind error");
            close(sockfd);
            return -1;
        }
    
        //3、监听
        if(listen(sockfd,5)<0)
        {
            perror("listen error");
            close(sockfd);
            return -1;
        }
        while(1)
        {
            int n_sockfd;
            struct sockaddr_in cli;
            //4、accept
            n_sockfd=accept(sockfd,(struct sockaddr*)&cli,&len);
            if(n_sockfd<0){
                perror("accept error");
                continue;
            }
            //5、recv
            while(1){
                //6、send
                char buff[1024]={0};
                int ret=recv(n_sockfd,buff,1023,0);
                if(ret<0){
                    perror("recv error");
                    continue;
                }
                printf("client %s[%d]say:%s",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port),buff);
    
                memset(buff,0x00,1024);
                scanf("%s",buff);
                send(n_sockfd,buff,sizeof(buff),0);
                //7、close(sockfd)
            }
            close(n_sockfd);
        }
    }
    

    TCP客户端

    不推荐手动绑定地址
    创建套接字——>connect——>send——>recv——>close。

    //tcp客户端端的代码
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<errno.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    
    int main()
    {
        //1、创建socket套接字
        int sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(sockfd<0){
            perror("socket error");
            return -1;
        }
        struct sockaddr_in ser;
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi("9000"));
        ser.sin_addr.s_addr=inet_addr("192.168.117.130");
        //2、连接
        socklen_t len=sizeof(struct sockaddr_in);
        if(connect(sockfd,(struct sockaddr*)&ser,len)<0)
        {
            perror("connect error");
            return -1;
        }
        //3、发送数据
        while(1)
        {
                char buff[1024]={0};
                scanf("%s",buff);
                send(sockfd,buff,sizeof(buff),0);
                memset(buff,0x00,1024);
    
                int ret=recv(sockfd,buff,1023,0);
                if(ret<0){
                    perror("recv error");
                    continue;
                }
                printf("client %s[%d]say:%s",inet_ntoa(ser.sin_addr),ntohs(ser.sin_port),buff);
        }
        close(sockfd);
    }
    

    在这里插入图片描述

    展开全文
    Vickers_xiaowei 2019-01-14 23:16:26
  • 5星
    23KB tangcaijun 2013-08-13 23:57:50
  • tcp_syn_retries :INTEGER 默认值是5 对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2....
    tcp_syn_retries :INTEGER
    
    默认值是5
    对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,是由tcp_retries1 决定的)

    tcp_synack_retries :INTEGER
    默认值是5
    对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。(可以根据上面的tcp_syn_retries来决定这个值)

    tcp_keepalive_time :INTEGER
    默认值是7200(2小时)
    当keepalive打开的情况下,TCP发送keepalive消息的频率。(由于目前网络攻击等因素,造成了利用这个进行的攻击很频繁,曾经也有cu的朋友提到过,说如果2边建立了连接,然后不发送任何数据或者rst/fin消息,那么持续的时间是不是就是2小时,空连接攻击?tcp_keepalive_time就是预防此情形的.我个人在做nat服务的时候的修改值为1800秒)

    tcp_keepalive_probes:INTEGER
    默认值是9
    TCP发送keepalive探测以确定该连接已经断开的次数。(注意:保持连接仅在SO_KEEPALIVE套接字选项被打开是才发送.次数默认不需要修改,当然根据情形也可以适当地缩短此值.设置为5比较合适)

    tcp_keepalive_intvl:INTEGER
    默认值为75
    探测消息发送的频率,乘以tcp_keepalive_probes就得到对于从开始探测以来没有响应的连接杀除的时间。默认值为75秒,也就是没有活动的连接将在大约11分钟以后将被丢弃。(对于普通应用来说,这个值有一些偏大,可以根据需要改小.特别是web类服务器需要改小该值,15是个比较合适的值)

    tcp_retries1 :INTEGER
    默认值是3
    放弃回应一个TCP连接请求前﹐需要进行多少次重试。RFC 规定最低的数值是3﹐这也是默认值﹐根据RTO的值大约在3秒 - 8分钟之间。(注意:这个值同时还决定进入的syn连接)

    tcp_retries2 :INTEGER
    默认值为15
    在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试。默认值为15,根据RTO的值来决定,相当于13-30分钟(RFC1122规定,必须大于100秒).(这个值根据目前的网络设置,可以适当地改小,我的网络内修改为了5)

    tcp_orphan_retries :INTEGER
    默认值是7
    在近端丢弃TCP连接之前﹐要进行多少次重试。默认值是7个﹐相当于 50秒 - 16分钟﹐视 RTO 而定。如果您的系统是负载很大的web服务器﹐那么也许需要降低该值﹐这类 sockets 可能会耗费大量的资源。另外参的考tcp_max_orphans 。(事实上做NAT的时候,降低该值也是好处显著的,我本人的网络环境中降低该值为3)

    tcp_fin_timeout :INTEGER
    默认值是 60
    对于本端断开的socket连接,TCP保持在FIN-WAIT-2状态的时间。对方可能会断开连接或一直不结束连接或不可预料的进程死亡。默认值为 60 秒。过去在2.2版本的内核中是 180 秒。您可以设置该值﹐但需要注意﹐如果您的机器为负载很重的web服务器﹐您可能要冒内存被大量无效数据报填满的风险﹐FIN-WAIT-2 sockets 的危险性低于 FIN-WAIT-1 ﹐因为它们最多只吃 1.5K 的内存﹐但是它们存在时间更长。另外参考 tcp_max_orphans。(事实上做NAT的时候,降低该值也是好处显著的,我本人的网络环境中降低该值为30)

    tcp_max_tw_buckets :INTEGER
    默认值是180000
    系统在同时所处理的最大 timewait sockets 数目。如果超过此数的话﹐time-wait socket 会被立即砍除并且显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要人为的降低这个限制﹐不过﹐如果网络条件需要比默认值更多﹐则可以提高它(或许还要增加内存)。(事实上做NAT的时候最好可以适当地增加该值)

    tcp_tw_recycle :BOOLEAN
    默认值是0
    打开快速 TIME-WAIT sockets 回收。除非得到技术专家的建议或要求﹐请不要随意修改这个值。(做NAT的时候,建议打开它)

    tcp_tw_reuse:BOOLEAN
    默认值是0
    该文件表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接(这个对快速重启动某些服务,而启动后提示端口已经被使用的情形非常有帮助)

    tcp_max_orphans :INTEGER
    缺省值是8192
    系统所能处理不属于任何进程的TCP sockets最大数量。假如超过这个数量﹐那么不属于任何进程的连接会被立即reset,并同时显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要依赖这个或是人为的降低这个限制(这个值Redhat AS版本中设置为32768,但是很多防火墙修改的时候,建议该值修改为2000)

    tcp_abort_on_overflow :BOOLEAN
    缺省值是0
    当守护进程太忙而不能接受新的连接,就象对方发送reset消息,默认值是false。这意味着当溢出的原因是因为一个偶然的猝发,那么连接将恢复状态。只有在你确信守护进程真的不能完成连接请求时才打开该选项,该选项会影响客户的使用。(对待已经满载的sendmail,apache这类服务的时候,这个可以很快让客户端终止连接,可以给予服务程序处理已有连接的缓冲机会,所以很多防火墙上推荐打开它)

    tcp_syncookies :BOOLEAN
    默认值是0
    只有在内核编译时选择了CONFIG_SYNCOOKIES时才会发生作用。当出现syn等候队列出现溢出时象对方发送syncookies。目的是为了防止syn flood攻击。
    注意:该选项千万不能用于那些没有收到攻击的高负载服务器,如果在日志中出现synflood消息,但是调查发现没有收到synflood攻击,而是合法用户的连接负载过高的原因,你应该调整其它参数来提高服务器性能。参考:
    tcp_max_syn_backlog
    tcp_synack_retries
    tcp_abort_on_overflow
    syncookie严重的违背TCP协议,不允许使用TCP扩展,可能对某些服务导致严重的性能影响(如SMTP转发)。(注意,该实现与BSD上面使用的tcp proxy一样,是违反了RFC中关于tcp连接的三次握手实现的,但是对于防御syn-flood的确很有用.)

    tcp_stdurg :BOOLEAN
    默认值为0
    使用 TCP urg pointer 字段中的主机请求解释功能。大部份的主机都使用老旧的 BSD解释,因此如果您在 Linux 打开它﹐或会导致不能和它们正确沟通。

    tcp_max_syn_backlog :INTEGER
    对于那些依然还未获得客户端确认的连接请求﹐需要保存在队列中最大数目。对于超过 128Mb 内存的系统﹐默认值是1024 ﹐低于 128Mb 的则为 128。如果服务器经常出现过载﹐可以尝试增加这个数字。警告﹗假如您将此值设为大于1024﹐最好修改 include/net/tcp.h 里面的 TCP_SYNQ_HSIZE ﹐以保持TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog ﹐并且编进核心之内。(SYN Flood攻击利用TCP协议散布握手的缺陷,伪造虚假源IP地址发送大量TCP-SYN半打开连接到目标系统,最终导致目标系统Socket队列资源耗尽而无法接受新的连接。为了应付这种攻击,现代Unix系统中普遍采用多连接队列处理的方式来缓冲(而不是解决)这种攻击,是用一个基本队列处理正常的完全连接应用(Connect()和Accept() ),是用另一个队列单独存放半打开连接。这种双队列处理方式和其他一些系统内核措施(例如Syn-Cookies/Caches)联合应用时,能够比较有效的缓解小规模的SYN Flood攻击(事实证明<1000p/s)加大SYN队列长度可以容纳更多等待连接的网络连接数,所以对Server来说可以考虑增大该值.)

    tcp_window_scaling :INTEGER
    缺省值为1
    该文件表示设置tcp/ip会话的滑动窗口大小是否可变。参数值为布尔值,为1时表示可变,为0时表示不可变。tcp/ip通常使用的窗口最大可达到 65535 字节,对于高速网络,该值可能太小,这时候如果启用了该功能,可以使tcp/ip滑动窗口大小增大数个数量级,从而提高数据传输的能力(RFC 1323)。(对普通地百M网络而言,关闭会降低开销,所以如果不是高速网络,可以考虑设置为0)

    tcp_timestamps :BOOLEAN
    缺省值为1
    Timestamps 用在其它一些东西中﹐可以防范那些伪造的 sequence 号码。一条1G的宽带线路或许会重遇到带 out-of-line数值的旧sequence 号码(假如它是由于上次产生的)。Timestamp 会让它知道这是个 '旧封包'。(该文件表示是否启用以一种比超时重发更精确的方法(RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。)

    tcp_sack :BOOLEAN
    缺省值为1

    使用 Selective ACK﹐它可以用来查找特定的遗失的数据报--- 因此有助于快速恢复状态。该文件表示是否启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段)。(对于广域网通信来说这个选项应该启用,但是这会增加对 CPU 的占用。)

    关于 TCP 协议中的SACK,请见这篇博文

    http://blog.csdn.net/bigtree_3721/article/details/51320422



    tcp_fack :BOOLEAN
    缺省值为1
    打开FACK拥塞避免和快速重传功能。(注意,当tcp_sack设置为0的时候,这个值即使设置为1也无效)

    tcp_dsack :BOOLEAN
    缺省值为1
    允许TCP发送"两个完全相同"的SACK。

    tcp_ecn :BOOLEAN
    缺省值为0
    打开TCP的直接拥塞通告功能。

    tcp_reordering :INTEGER
    默认值是3
    TCP流中重排序的数据报最大数量 。 (一般有看到推荐把这个数值略微调整大一些,比如5)

    tcp_retrans_collapse :BOOLEAN
    缺省值为1
    对于某些有bug的打印机提供针对其bug的兼容性。(一般不需要这个支持,可以关闭它)

    tcp_wmem(3个INTEGER变量): min, default, max
    min:为TCP socket预留用于发送缓冲的内存最小值。每个tcp socket都可以在建议以后都可以使用它。默认值为4096(4K)。

    default:为TCP socket预留用于发送缓冲的内存数量,默认情况下该值会影响其它协议使用的net.core.wmem_default 值,一般要低于net.core.wmem_default的值。默认值为16384(16K)。

    max: 用于TCP socket发送缓冲的内存最大值。该值不会影响net.core.wmem_max,"静态"选择参数SO_SNDBUF则不受该值影响。默认值为131072(128K)。(对于服务器而言,增加这个参数的值对于发送数据很有帮助,在我的网络环境中,修改为了51200 131072 204800)

    tcp_rmem (3个INTEGER变量): min, default, max
    min:为TCP socket预留用于接收缓冲的内存数量,即使在内存出现紧张情况下tcp socket都至少会有这么多数量的内存用于接收缓冲,默认值为8K。

    default:为TCP socket预留用于接收缓冲的内存数量,默认情况下该值影响其它协议使用的net.core.wmem_default 值。该值决定了在tcp_adv_win_scale、tcp_app_win和tcp_app_win=0默认值情况下,TCP窗口大小为65535。默认值为87380

    max:用于TCP socket接收缓冲的内存最大值。该值不会影响 net.core.wmem_max,"静态"选择参数 SO_SNDBUF则不受该值影响。默认值为 128K。默认值为87380*2 bytes。(可以看出,.max的设置最好是default的两倍,对于NAT来说主要该增加它,我的网络里为 51200 131072 204800)

    tcp_mem(3个INTEGER变量):low, pressure, high
    low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。(理想情况下,这个值应与指定给 tcp_wmem 的第 2 个值相匹配 - 这第 2 个值表明,最大页面大小乘以最大并发请求数除以页大小 (131072 * 300 / 4096)。 )

    pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。(理想情况下这个值应该是 TCP 可以使用的总缓冲区大小的最大值 (204800 * 300 / 4096)。 )

    high:允许所有tcp sockets用于排队缓冲数据报的页面量。(如果超过这个值,TCP 连接将被拒绝,这就是为什么不要令其过于保守 (512000 * 300 / 4096) 的原因了。在这种情况下,提供的价值很大,它能处理很多连接,是所预期的 2.5 倍;或者使现有连接能够传输 2.5 倍的数据。我的网络里为192000 300000 732000)

    一般情况下这些值是在系统启动时根据系统内存数量计算得到的。

    tcp_app_win : INTEGER
    默认值是31
    保留max(window/2^tcp_app_win, mss)数量的窗口由于应用缓冲。当为0时表示不需要缓冲。

    tcp_adv_win_scale : INTEGER
    默认值为2
    计算缓冲开销bytes/2^tcp_adv_win_scale(如果tcp_adv_win_scale > 0)或者bytes-bytes/2^(-tcp_adv_win_scale)(如果tcp_adv_win_scale <= 0)。

    tcp_rfc1337 :BOOLEAN
    缺省值为0
    这个开关可以启动对于在RFC1337中描述的"tcp 的time-wait暗杀危机"问题的修复。启用后,内核将丢弃那些发往time-wait状态TCP套接字的RST 包.

    tcp_low_latency : BOOLEAN
    缺省值为0
    允许 TCP/IP 栈适应在高吞吐量情况下低延时的情况;这个选项一般情形是的禁用。(但在构建Beowulf 集群的时候,打开它很有帮助)

    tcp_westwood :BOOLEAN
    缺省值为0
    启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。

    tcp_bic :BOOLEAN
    缺省值为0
    为快速长距离网络启用 Binary Increase Congestion;这样可以更好地利用以 GB 速度进行操作的链接;对于 WAN 通信应该启用这个选项。


    另外缺省的一些socket参数如下:


    CLOSE_WAIT

     就是 tcp_keepalive_tim, 缺省是7200秒

    TIME_WAIT 

    TIME_WAIT 是主动关闭链接时形成的,等待2MSL时间, 协议规定是240秒,主要是防止最后一个ACK丢失。  由于TIME_WAIT 的时间会非常长,因此server端应尽量减少主动关闭连接.

    有个 sysctl 参数貌似可以使用,它是 /proc/sys/net/ipv4/tcp_fin_timeout,缺省值是 60,也就是60秒,很多网上的资料都说将这个数值设置低一些就可以减少netstat 里面的TIME_WAIT状态,但是这个说法不是很准确的。经过认真阅读Linux的内核源代码,我们发现这个数值其实是输出用的,修改之后并没有真正的读回内核中进行使用,而内核中真正管用的是一个宏定义,在 $KERNEL/include/net/tcp.h里面,有下面的行:

          #define TCP_TIMEWAIT_LEN (60*HZ)
          而这个宏是真正控制 TCP TIME_WAIT 状态的超时时间的。如果我们希望减少 TIME_WAIT 状态的数目(从而节省一点点内核操作时间),那么可以把这个数值设置低一些,根据我们的测试,设置为 10 秒比较合适,也就是把上面的修改为:
          #define TCP_TIMEWAIT_LEN (10*HZ)

      然后重新编译内核,重启系统即可发现短连接造成的TIME_WAIT状态大大减少:
      netstat -ant | grep -i time_wait |wc -l
      一般情况都可以至少减少2/3。也能相应提高系统应对短连接的速度



    展开全文
    bigtree_3721 2016-04-30 00:37:41
  • 修改linux tcp连接数
                   

    一、 文件数限制修改
    (1) vi /etc/security/limits.conf
    * soft nofile 10240
    * hard nofile 10240

    (2) vi /etc/pam.d/login
    session required /lib/security/pam_limits.so

    二、 网络端口限制修改
    (1) vi /etc/rc.d/rc.local
    /sbin/modprobe ip_conntrack # 加载 ip_contrack 模块
    # /sbin/sysctl –p # 使 /etc/sysctl.conf 的配置生效,根据实际情况来决定是否添加此命令

    [root@AS4U8 ~]# sysctl -a | grep "net.ipv4.ip"
    net.ipv4.ip_conntrack_max = 16384
    这表明将系统对最大跟踪的TCP连接数限制默认为16384。请注意,此限制值要尽量小,以节省对内核内存的占用

    (2) vi /etc/sysctl.conf
    net.ipv4.ip_local_port_range = 1024 65000
    net.ipv4.ip_conntrack_max = 10240
    此限制值要尽量小,以节省对内核内存的占用。

    详细解说:
    1、修改用户进程可打开文件数限制
    在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄)。可使用ulimit命令查看系统允许当前用户进程打开的文件数限制:
    [speng@as4 ~]$ ulimit -n
    1024
    这表示当前用户的每个进程最多允许同时打开1024个文件,这1024个文件中还得除去每个进程必然打开的标准输入,标准输出,标准错误,服务器监听socket,进程间通讯的unix域socket等文件,那么剩下的可用于客户端socket连接的文件数就只有大概1024-10=1014个左右。也就是说缺省情况下,基于Linux的通讯程序最多允许同时1014个TCP并发连接。

    对于想支持更高数量的TCP并发连接的通讯处理程序,就必须修改Linux对当前用户的进程同时打开的文件数量的软限制(soft limit)和硬限制(hardlimit)。其中软限制是指Linux在当前系统能够承受的范围内进一步限制用户同时打开的文件数;硬限制则是根据系统硬件资源状况(主要是系统内存)计算出来的系统最多可同时打开的文件数量。通常软限制小于或等于硬限制。

    修改上述限制的最简单的办法就是使用ulimit命令:
    [speng@as4 ~]$ ulimit -n 
    上述命令中,在中指定要设置的单一进程允许打开的最大文件数。如果系统回显类似于“Operation notpermitted”之类的话,说明上述限制修改失败,实际上是因为在中指定的数值超过了Linux系统对该用户打开文件数的软限制或硬限制。因此,就需要修改Linux系统对用户的关于打开文件数的软限制和硬限制。

    第一步,修改/etc/security/limits.conf文件,在文件中添加如下行:
    speng soft nofile 10240
    speng hard nofile 10240
    其中speng指定了要修改哪个用户的打开文件数限制,可用'*'号表示修改所有用户的限制;soft或hard指定要修改软限制还是硬限制;10240则指定了想要修改的新的限制值,即最大打开文件数(请注意软限制值要小于或等于硬限制)。修改完后保存文件。

    第二步,修改/etc/pam.d/login文件,在文件中添加如下行:
    session required /lib/security/pam_limits.so
    这是告诉Linux在用户完成系统登录后,应该调用pam_limits.so模块来设置系统对该用户可使用的各种资源数量的最大限制(包括用户可打开的最大文件数限制),而pam_limits.so模块就会从/etc/security/limits.conf文件中读取配置来设置这些限制值。修改完后保存此文件。

    第三步,查看Linux系统级的最大打开文件数限制,使用如下命令:
    [speng@as4 ~]$ cat /proc/sys/fs/file-max
    12158
    这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)12158个文件,是Linux系统级硬限制,所有用户级的打开文件数限制都不应超过这个数值。通常这个系统级硬限制是Linux系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制,如果没有特殊需要,不应该修改此限制,除非想为用户级打开文件数限制设置超过此限制的值。修改此硬限制的方法是修改/etc/rc.local脚本,在脚本中添加如下行:
    echo 22158 > /proc/sys/fs/file-max
    这是让Linux在启动完成后强行将系统级打开文件数硬限制设置为22158。修改完后保存此文件。

    完成上述步骤后重启系统,一般情况下就可以将Linux系统对指定用户的单一进程允许同时打开的最大文件数限制设为指定的数值。如果重启后用ulimit-n命令查看用户可打开文件数限制仍然低于上述步骤中设置的最大值,这可能是因为在用户登录脚本/etc/profile中使用ulimit-n命令已经将用户可同时打开的文件数做了限制。由于通过ulimit-n修改系统对用户可同时打开文件的最大数限制时,新修改的值只能小于或等于上次ulimit-n设置的值,因此想用此命令增大这个限制值是不可能的。所以,如果有上述问题存在,就只能去打开/etc/profile脚本文件,在文件中查找是否使用了ulimit-n限制了用户可同时打开的最大文件数量,如果找到,则删除这行命令,或者将其设置的值改为合适的值,然后保存文件,用户退出并重新登录系统即可。
    通过上述步骤,就为支持高并发TCP连接处理的通讯处理程序解除关于打开文件数量方面的系统限制。

    2、修改网络内核对TCP连接的有关限制

    在Linux上编写支持高并发TCP连接的客户端通讯处理程序时,有时会发现尽管已经解除了系统对用户同时打开文件数的限制,但仍会出现并发TCP连接数增加到一定数量时,再也无法成功建立新的TCP连接的现象。出现这种现在的原因有多种。

    第一种原因可能是因为Linux网络内核对本地端口号范围有限制。此时,进一步分析为什么无法建立TCP连接,会发现问题出在connect()调用返回失败,查看系统错误提示消息是“Can't assign requestedaddress”。同时,如果在此时用tcpdump工具监视网络,会发现根本没有TCP连接时客户端发SYN包的网络流量。这些情况说明问题在于本地Linux系统内核中有限制。其实,问题的根本原因在于Linux内核的TCP/IP协议实现模块对系统中所有的客户端TCP连接对应的本地端口号的范围进行了限制(例如,内核限制本地端口号的范围为1024~32768之间)。当系统中某一时刻同时存在太多的TCP客户端连接时,由于每个TCP客户端连接都要占用一个唯一的本地端口号(此端口号在系统的本地端口号范围限制中),如果现有的TCP客户端连接已将所有的本地端口号占满,则此时就无法为新的TCP客户端连接分配一个本地端口号了,因此系统会在这种情况下在connect()调用中返回失败,并将错误提示消息设为“Can't assignrequested address”。有关这些控制逻辑可以查看Linux内核源代码,以linux2.6内核为例,可以查看tcp_ipv4.c文件中如下函数:
    static int tcp_v4_hash_connect(struct sock *sk)
    请注意上述函数中对变量sysctl_local_port_range的访问控制。变量sysctl_local_port_range的初始化则是在tcp.c文件中的如下函数中设置:
    void __init tcp_init(void)
    内核编译时默认设置的本地端口号范围可能太小,因此需要修改此本地端口范围限制。
    第一步,修改/etc/sysctl.conf文件,在文件中添加如下行:
    net.ipv4.ip_local_port_range = 1024 65000
    这表明将系统对本地端口范围限制设置为1024~65000之间。请注意,本地端口范围的最小值必须大于或等于1024;而端口范围的最大值则应小于或等于65535。修改完后保存此文件。
    第二步,执行sysctl命令:
    [speng@as4 ~]$ sysctl -p
    如果系统没有错误提示,就表明新的本地端口范围设置成功。如果按上述端口范围进行设置,则理论上单独一个进程最多可以同时建立60000多个TCP客户端连接。

    第二种无法建立TCP连接的原因可能是因为Linux网络内核的IP_TABLE防火墙对最大跟踪的TCP连接数有限制。此时程序会表现为在connect()调用中阻塞,如同死机,如果用tcpdump工具监视网络,也会发现根本没有TCP连接时客户端发SYN包的网络流量。由于IP_TABLE防火墙在内核中会对每个TCP连接的状态进行跟踪,跟踪信息将会放在位于内核内存中的conntrackdatabase中,这个数据库的大小有限,当系统中存在过多的TCP连接时,数据库容量不足,IP_TABLE无法为新的TCP连接建立跟踪信息,于是表现为在connect()调用中阻塞。此时就必须修改内核对最大跟踪的TCP连接数的限制,方法同修改内核对本地端口号范围的限制是类似的:
    第一步,修改/etc/sysctl.conf文件,在文件中添加如下行:
    net.ipv4.ip_conntrack_max = 10240
    这表明将系统对最大跟踪的TCP连接数限制设置为10240。请注意,此限制值要尽量小,以节省对内核内存的占用。
    第二步,执行sysctl命令:
    [speng@as4 ~]$ sysctl -p
    如果系统没有错误提示,就表明系统对新的最大跟踪的TCP连接数限制修改成功。如果按上述参数进行设置,则理论上单独一个进程最多可以同时建立10000多个TCP客户端连接。

    3、使用支持高并发网络I/O的编程技术

    在Linux上编写高并发TCP连接应用程序时,必须使用合适的网络I/O技术和I/O事件分派机制。

    可用的I/O技术有同步I/O,非阻塞式同步I/O(也称反应式I/O),以及异步I/O。在高TCP并发的情形下,如果使用同步I/O,这会严重阻塞程序的运转,除非为每个TCP连接的I/O创建一个线程。但是,过多的线程又会因系统对线程的调度造成巨大开销。因此,在高TCP并发的情形下使用同步I/O是不可取的,这时可以考虑使用非阻塞式同步I/O或异步I/O。非阻塞式同步I/O的技术包括使用select(),poll(),epoll等机制。异步I/O的技术就是使用AIO。

    从I/O事件分派机制来看,使用select()是不合适的,因为它所支持的并发连接数有限(通常在1024个以内)。如果考虑性能,poll()也是不合适的,尽管它可以支持的较高的TCP并发数,但是由于其采用“轮询”机制,当并发数较高时,其运行效率相当低,并可能存在I/O事件分派不均,导致部分TCP连接上的I/O出现“饥饿”现象。而如果使用epoll或AIO,则没有上述问题(早期Linux内核的AIO技术实现是通过在内核中为每个I/O请求创建一个线程来实现的,这种实现机制在高并发TCP连接的情形下使用其实也有严重的性能问题。但在最新的Linux内核中,AIO的实现已经得到改进)。

    综上所述,在开发支持高并发TCP连接的Linux应用程序时,应尽量使用epoll或AIO技术来实现并发的TCP连接上的I/O控制,这将为提升程序对高并发TCP连接的支持提供有效的I/O保证。

    修改linux socket最大连接数

    最简单作法:
    打开/etc/security/limits.conf
    * soft nofile 51200
    * hard nofile 51200

               

    再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

    展开全文
    tyuuhgf 2019-01-21 12:44:20
  • tjf1314520 2017-03-01 12:54:25
  • zhangjunli 2019-03-28 15:12:49
  • 3星
    13.31MB huanglin529 2018-03-13 10:39:44
  • 224KB fanxiushu 2016-02-03 23:56:34
  • u011270542 2018-04-25 13:45:16
  • chenlin465373800 2017-12-28 17:48:21
  • qq_43089516 2019-06-20 16:19:57
  • macky0668 2011-09-22 23:41:18
  • robertsong2004 2014-12-04 16:00:11
  • u010032342 2017-01-11 21:02:36
  • chenyulancn 2013-11-15 13:58:56
  • Just_shunjian 2017-10-19 19:03:20
  • saga_gallon 2017-02-22 10:03:59
  • kyee 2016-07-25 14:40:56
  • jsh13417 2013-08-26 10:39:45
  • weixin_44894312 2020-08-24 17:51:59
  • u012365828 2014-05-20 09:46:40
  • fz835304205 2013-11-20 15:17:11
  • chenyefei 2016-12-01 09:49:40
  • Genius_LG 2014-07-22 19:20:57

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 33,728
精华内容 13,491
关键字:

linuxtcp通讯类

linux 订阅