socket编程
来源:互联网 发布:sql优化书籍推荐知乎 编辑:程序博客网 时间:2024/05/21 19:30
13. sockaddr_in清空方式
初始化可直接清空
sockaddr_in addr = {0};
1.TIME_WAIT和FIN_WAIT
问题:一方close(fd1),另一方没有close(fd2),导致调用方fd1处于TIME_WAIT和FIN_WAIT,socket不能使用。
解决方法:struct linger ling = {1,0} 跳过四步握手,直接强杀socket,socket直接CLOSED呈可用状态。
tcp连接的关闭经过FIN->ACK->FIN->ACK四步握手。
服务器先调用close(socket):
客户端先调用close(socket):
过程:
1.A调用close(socket)发送FIN表示A的数据传输完成、没有其他数据要传输给B
2.B发ACK对FIN进行应答
3.A进入FIN_WAIT_2、B进入CLOSE_WAIT
4.此时B仍旧可以发送数据给A(A的事儿处理完了不代表B的事儿也处理完了)
5.B把其余数据发送给A完成传输
6.B调用close(socket)发FIN
7.A发ACK予以响应
8.连接关闭
一个close()对应着一个FIN,一个FIN对应着一个close()调用。只有双方都使用close关闭socket后,连接才算关闭,以上就是以绅士的方式关闭TCP连接。但是不管是谁,率先调用close()的一方必回最终处于TIME_WAIT状态,在一段时间后方可变为CLOSED状态,此状态下的socket表可用。
在某些情况下,比如由服务器主动关闭连接的情况,如果client足够好调用了close(),则服务器端会有大量socket处于TIME_WAIT状态。如果client非善类,不曾调用close(),则服务器会有大量socket处于FIN_WAIT2状态下。这两种情况最终将会导致资源耗尽。
CentOS6.4 系统下为了解决这一问题,先后试着更改系统文件,close()前先调用shutdown()强制关闭,均不起作用。
最终解决办法如下:
server每接受一个新连接,即对生成的socket进行参数设置:
struct linger ling = {1,0};if(setsockopt(fd,SOL_SOCKET,SO_LINGER,(void*)&ling,sizeof(ling)) !=0){std::cout<<"set socket fail"<<std::endl;return;}SO_LINGER显示需设置的选项,struct linger结构:
struct linger {int l_onoff; //0即关闭此选项,1即打开此选项int l_linger; //逗留时间};
有下列三种情况:
a、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据;
b、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT和FIN_WAIT2状态;
c、设置 l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。
所以当设置ling = {1,0}就会扔掉缓冲区中的数据、跳过四步握手、直接进入CLOSED。
编写客户端程序,不断创建socket、connect服务器、收发数据,无限循环,不关闭socket。socket达到1024即出现:Too many open files, 原来一个程序不能打开过多socket。ulimit -a 可以查看到“open files (-n) 1024”或者直接ulimit -n。
通常为1024,可以通过ulimit -n 65535修改为任意值。
ulimit -n num 只对当前窗口、当前shell有效。
显示网络连接:
netstat -antp | grep tcp |less
-a 显示所有socket
-n 显示网络ip地址
-t ·显示tcp协议连接状态
-p 显示指定协议信息
FIN2_WAIT和CLOSE_WAIT、TIME_WAIT都没有了。
服务器端send完数据后,马上close套接字,小试验暂时并无问题。可以想见若服务器端压力过大,数据进入缓冲区未及发送,此时的close()将意味着数据被抛弃。
2.服务器重启 Address already in use
当客户端保持着与服务器端的连接,这时服务器意外关闭,再开启服务器时会出现: Address already in use。
可以用netstat -anp | more 可以看到客户端还保持着与服务器的连接(还在使用服务器bind的端口)。这是由于client没有执行close,连接还会等待client的FIN包一段时间。
解决方法是使用setsockopt,使得socket可以被重用,是最常用的服务器编程要点。
具体的做法为是,在socket调用和bind 调用之间加上一段对socket的设置:
int flag = 1;if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, sizeof(flag)) != 0){ std::cout<<"set addr reuse fail : "<<strerror(errno)<<std::endl; return -1;}
3.把socket设置成非阻塞:
- int flags;
- if((flags = fcntl(fd, F_GETFL)) < 0) //获取当前的flags标志
- err_sys(“F_GETFL error!”);
- flags |= O_NONBLOCK; //修改非阻塞标志位
- if(fcntl(fd, F_SETFL, flags) < 0)
- err_sys(“F_SETFL error!”);
4.close(fd);关闭socket后,连接断开,socket被销毁。
Aconnect到B,B发生异常,close(fd2)断开连接,A端socket收到连接异常。A要进行重连必须按如下顺序操作:
a.close(fd1) 关闭自身socket
b.socket() 创建新socket
c.connect() 重连
5.创建socket套接字:
/* 函数原型 */
int socket(intdomain,inttype,intprotocol);
/* 头文件 */
#include <sys/types.h>
#include <sys/socket.h>
6.设置socket:
#include <sys/types.h>#include <sys/socket.h>struct sockaddr_in sin;memset(&sin,0,sizeof(sin));sin.sin_family = AF_INET;sin.sin_addr.s_addr = htonl(INADDR_ANY);sin.sin_port = htons(8080);
htonl(INADDR_ANY)等价于inet_addr(“0.0.0.0”),表示本机所有地址,在本机网卡有多个或者替换网卡时就不用特意修改了。
而inet_addr(“127.0.0.1”)多用于测试使用,telnet 192.168.15.12 8080就会出现“connection refused”。
7.inet_addr头函数#include<arpa/inet.h>
8.从sockaddr中取ip和port方法:
struct sockaddr_in sin;
memcpy(&sin,addr,sizeof(sin));
sockaddr和sockaddr_in可以相互类型转换,故简单方法如下:
struct sockaddr_in *sin = (struct sockaddr_in*)addr;
然后:
std::cout<<"ip : "<<inet_ntoa(sin->sin_addr)<<std::endl;
std::cout<<"port : "<<ntohs(sin->sin_port)<<std::endl;
9.设置socket缓冲区
int bufLen = 100 * 1024; int ret = setsockopt(Socket,SOL_SOCKET,SO_RCVBUF,(char *)&bufLen,sizeof(bufLen)); if(ret != 0){std::cout<<"Set Socket Buffer Fail: "<<GetLastError()<<std::endl;return -1;}
10. recvfrom : GetLastError() = 10040
recvfrom成功返回0;错误返回-1,GetLastError()返回错误码。
若错误码为10040,很可能是用于接收数据的自定义缓冲区小了的缘故。
11.socket设置非阻塞
设置为非阻塞:
unsigned long ul = 1;
int ret = ioctlsocket(s, FIONBIO, &ul);
设置为阻塞:
unsigned long ul = 0;
int ret = ioctlsocket(s, FIONBIO, &ul);
返回值:
if(SOCKET_ERROR == ret)
{
std::cout<<"ioctlsocket Error : "<<GetLastError()<<std::endl;
closesocket(s);
s = INVALID_SOCKET_VALUE;
return false;
}
12.socket编程实例:
server
#include <iostream>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h> //htons() htonl()#include <errno.h> //errno#include <string.h> //strerror()#include <unistd.h> //sleep()int main(){ struct sockaddr_in sin; socklen_t slen; int fd, cfd; int data_len; char buffer[30] = {0}; fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0){ std::cout<<"create socket fail"<<std::endl; return -1; } struct linger ling = {1, 0}; /*if(setsockopt(fd, SOL_SOCKET, SO_LINGER, (void*)&ling, sizeof(ling)) != 0){ std::cout<<"set linger fail : "<<strerror(errno)<<std::endl; return -1; }*/ int flag = 1; if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, sizeof(flag)) != 0){ std::cout<<"set addr reuse fail : "<<strerror(errno)<<std::endl; return -1; } sin.sin_family = AF_INET; sin.sin_port = htons(6666); sin.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) != 0){ std::cout<<"bind fail : "<<strerror(errno)<<std::endl; return -1; } if(listen(fd, 50) != 0){ std::cout<<"listen fail"<<std::endl; return -1; } slen = sizeof(sin); cfd = accept(fd, (sockaddr*)&sin, &slen); if(cfd < 0){ std::cout<<"accept fail"<<std::endl; return -1; } data_len = recv(cfd, buffer, sizeof(buffer), 0); if(data_len <= 0){ if(data_len == 0) std::cout<<"client close socket"<<std::endl; else std::cout<<"some error fail"<<std::endl; return -1; } buffer[data_len] = '\0'; std::cout<<buffer<<std::endl; return 0;}
client
int main(){ struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); sin.sin_port = htons(8080); int fd = socket(AF_INET, SOCK_STREAM, 0); if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0){ std::cout<<"connect fail"<<std::endl; return 1; } char buf[50] = {0}; sprintf(buf,"saldkfjasad"); if(send(fd,buf,sizeof(buf),0)<0){ std::cout<<"send fail"<<std::endl; return 1; } int len = recv(fd,buf,sizeof(buf),0); buf[len] = '\0'; std::cout<<buf<<std::endl; if(close(fd) != 0) std::cout<<"close socket fail"<<std::endl;}
- socket编程--socket基本概念
- socket编程--socket基本概念
- socket编程
- socket编程
- Socket 编程
- socket编程
- Socket编程
- Socket编程
- Socket编程
- Socket编程
- SOCKET编程
- socket编程
- Socket编程
- socket编程
- Socket 编程
- Socket 编程
- socket 编程
- socket编程
- C语言的静态函数
- iOS开发牛人博客收集
- ABAP 中 MOVE ... TO 和 WRITE ... TO 的区别
- Sort List
- 路再长也有尽头,别往回走
- socket编程
- 反编译工具JAD.exe使用,在MyEclipse 10 添加jadclipser 插件
- adt Failed to create the Java Virtual Machine.
- mysql c api简单连接池
- 反射望远镜的发展历程(1)
- hadoop常见错误及处理方法
- Insertion Sort List
- 导致电脑网速变慢的几个方面
- unable to read askpass response from '/usr/libexec/openssh/gnome-ssh-askpass