創建套接字及設置其屬性

 
客戶端服務端共享的:
在進行套接字創建的時候採用int sock = ::socket(PF_INET, SOCK_STREAM, 0);
設置套接口發送及接收緩衝大小:
socklen_t window_size = 128*1204
int retcode = ::setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &window_size, sizeof(window_size));
int retcode = ::setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &window_size, sizeof(window_size));
下面的可重用設置只有服務端才用的
int reuse = 1;
int retcode = ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));//設置套接口爲可重用狀態
接着來就是要connect了(客戶端採用的)
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str()); //服務端只改爲htonl(INADDR_ANY);即可
addr.sin_port = htons(port);
服務端必須要有的綁定(客戶端的在connect操作會自動綁定的,所以可以沒有)
int retcode = ::bind(sock, (struct sockaddr*) &addr, sizeof(addr));
服務端必須要有的監聽操作(客戶端是沒有的)
retcode = ::listen(sock, MAX_WAITQUEUE);
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = NULL;
assert(0 == epoll_ctl(kdpfd, EPOLL_CTL_ADD, sock, &ev));//kdpfd是由epoll_create產生的
­
retcode = TEMP_FAILURE_RETRY(::connect(sock, (struct sockaddr *)&addr, sizeof(addr));
現在如果conect success,那麼就進入進行對消息的收發的了
­
對於服務器還要進行::accept操作
int accept(struct sockaddr_in *addr)
{
socklen_t len = sizeof(struct sockaddr_in);
bzero(addr, sizeof(struct sockaddr_in));
struct epoll_event ev;
int rc = epoll_wait(kdfd, &ev, 1, timeout);
if(1 == rc && (ev.events & EPOLLIN))
return TEMP_FAILURE_RETRY(::accept(sock, (struct sockaddr *)addr, &len));
return -1;
}
­
retcode = TEMP_FAILURE_RETRY(::recv(sock, _rcv_queue.wr_buf(), _rcv_queue.wr_size(), MSG_NOSIGNAL));
如果在多線程的環境下,就會有多個線程同時對套接口進行讀數據,這個時候就需要一種策略來進行檢測套接口是否有數據可以讀取(function:waitForWrite)
strcut pollfd pfd;
pfd.fd = sock;
pfd.events = POLLIN | POLLOUT | POLLPRI;
pfd.revents = 0;
retcode = TEMP_FAILURE_RETRY(::poll(&pfd, 1, timeout));
if(retcode > 0 && 0 == (pfd.revents & POLLIN))//如果是檢測寫的(pfd.revnets & POLLOUT)
    retcode = -1;
對於發送數據需要進行數據量的控制(也就是流量的控制)
sendRawDataMI(const void *pBuffer, const int size)
{
if(NULL == pBuffer || size <= 0 ) return false;
int offset = 0;
do
{
int retcode = sendRawData(&((char *)pBuffer)[offset], size-offset);
if(-1 == retcode) return false;
offset += retcode;
}while(offset < size)
return (offset == size);
}
int sendRawData(const void *pBuffer, const int size)
{
if(isset_flag(INCOMPLETE_WRITE))
{
clear_flag(INCOMPLETE_WRITE);
goto do_select;
}
int retcode = TEMP_FAILURE_RETRY(::send(sock, pBuffer, size, MSG_NOSIGNAL));
if(retcode = -1 && (errno == EAGAIN) || errno == EWOULDBLOCK)
{
do_select:
retcode = waitForWrite();
if(1 == retcode)
retcode = TEMP_FAILURE_RETRY(::send(sock, pBuffer, size, MSG_NOSIGNAL));
else
return retcode;
}
if(retcode > 0 && retcode < size)
set_flag(INCOMPLETE_WRITE);
return retcode;
}
­
備註1:要對所創建的套接字進行合法性的檢測。
備註2:要對setstockopt函數返回值進行合法性的檢測
對於此函數的一點說明:
原型:
int setsockopt ( int sockfd, int level, int optname, const void * optval, socklen_t *opteln )
客戶端的必須要在::connect之前設置
服務端的必須要在::accept之前設置
sockfd(套接字): 指向一個打開的套接口描述字
level:(級別): 指定選項代碼的類型。
     SOL_SOCKET: 基本套接口
     IPPROTO_IP: IPv4套接口
     IPPROTO_IPV6: IPv6套接口
     IPPROTO_TCP: TCP套接口
     optname(選項名): 選項名稱
     optval(選項值): 是一個指向變量的指針 類型:整形,套接口結構, 其他結構類型
     optlen(選項長度) :optval 的大小
返回值:標誌打開或關閉某個特徵的二進制選項
­
SO_RCVBUF和SO_SNDBUF(選項)
每個套接口都有一個發送緩衝區和一個接收緩衝區。 接收緩衝區被TCP和UDP用來將接收到的數據一直保存到由應用進程來讀。 TCP:TCP通告另一端的窗口大小。 TCP套接口接收緩衝區不可能溢出,因爲對方不允許發出超過所通告窗口大小的數據。 這就是TCP的流量控制,如果對方無視窗口大小而發出了超過宙口大小的數據,則接 收方TCP將丟棄它。 UDP:當接收到的數據報裝不進套接口接收緩衝區時,此數據報就被丟棄。UDP是沒有 流量控制的;快的發送者可以很容易地就淹沒慢的接收者,導致接收方的UDP丟棄數據報。
備註3:
MSG_NOSIGNAL is a flag used by send()/recv() in some implementations of the Berkeley sockets
API.This flag requests that the implementation does not to send a SIGPIPE signal on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned as normal.
備註4:在進行數據的收發的時候採用了建立緩衝隊列&緩衝棧.
備註5:在上面的說明中kdpfd的定義:int kdpfd = epoll_create(1);
發佈了25 篇原創文章 · 獲贊 0 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章