tcp.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys types.h=""> #include <sys socket.h=""> #include <netinet in.h=""> #include <arpa inet.h=""> #include <fcntl.h> #include <errno.h> #include <linux tcp.h=""> #include <time.h> #include "tcp.h" /* FD_ZERO(fd_set *fdset) 將指定的文件描述符集清空,在對文件描述符集合進行設置前,必須對其進行初始化,如果不清空,由於在系統分配內存空間後,通常並不作清空處理,所以結果是不可知的。 FD_SET(fd_set *fdset) 用於在文件描述符集合中增加一個新的文件描述符。 FD_CLR(fd_set *fdset) 用於在文件描述符集合中刪除一個文件描述符。 FD_ISSET(int fd,fd_set *fdset) 用於測試指定的文件描述符是否在該集合中 */ int select_fd_init(fd_set *fdset) { FD_ZERO(fdset); return 0; } int select_fd_del(fd_set *fdset, int fd) { FD_CLR(fd, fdset); return 0; } int select_fd_add(fd_set *fdset, int fd) { FD_SET(fd, fdset); return 0; } //在集合中返回<0出錯,>0可讀寫,==0超時 int select_fd_check_r(fd_set *fdset_r, int fd, int sec, int usec) { /* 返回值:返回狀態發生變化的描述符總數。 負值:select錯誤 正值:某些文件可讀寫或出錯 0:等待超時,沒有可讀寫或錯誤的文件 */ struct timeval ptv; int ret; ptv.tv_sec = sec; ptv.tv_usec = usec; ret = select(fd + 1, fdset_r, NULL, NULL, &ptv); if(ret < 0){ return -1; }else if(ret > 0){ return 1; } return 0; } //在集合中返回<0出錯,>0可讀寫,==0超時 int select_fd_check_w(fd_set *fdset_w, int fd, int sec, int usec) { /* 返回值:返回狀態發生變化的描述符總數。 負值:select錯誤 正值:某些文件可讀寫或出錯 0:等待超時,沒有可讀寫或錯誤的文件 */ struct timeval ptv; int ret; ptv.tv_sec = sec; ptv.tv_usec = usec; ret = select(fd + 1, NULL, fdset_w, NULL, &ptv); if(ret < 0){ return -1; }else if(ret > 0){ return 1; } return 0; } //在集合中返回<0出錯,>0可讀寫,==0超時 int select_fd_check_rw(fd_set *fdset_r, fd_set *fdset_w, int fd, int sec, int usec) { /* 返回值:返回狀態發生變化的描述符總數。 負值:select錯誤 正值:某些文件可讀寫或出錯 0:等待超時,沒有可讀寫或錯誤的文件 */ struct timeval ptv; int ret; ptv.tv_sec = sec; ptv.tv_usec = usec; ret = select(fd + 1, fdset_r, fdset_w, NULL, &ptv); if(ret < 0){ return -1; }else if(ret > 0){ return 1; } return 0; } //當描述符fd在描述符集fdset中返回非零值,否則,返回零。 int select_fd_check2(fd_set *fdset, int fd) { return FD_ISSET(fd, fdset); } /* 返回值:返回狀態發生變化的描述符總數。 負值:select錯誤 正值:某些文件可讀寫或出錯 0:等待超時,沒有可讀寫或錯誤的文件 */ int select_fd_timeout_r(int fd, int sec, int usec) { struct timeval ptv; fd_set fdset; int ret; ptv.tv_sec = sec; ptv.tv_usec = usec; FD_ZERO(&fdset); FD_SET(fd, &fdset); ret = select(fd + 1, &fdset, NULL, NULL, &ptv); if(ret < 0){ return -1; }else if(ret > 0){ return 1; } return 0; } int select_fd_timeout_w(int fd, int sec, int usec) { struct timeval ptv; fd_set fdset; int ret; ptv.tv_sec = sec; ptv.tv_usec = usec; FD_ZERO(&fdset); FD_SET(fd, &fdset); ret = select(fd + 1, NULL, &fdset, NULL, &ptv); if(ret < 0){ return -1; }else if(ret > 0){ return 1; } return 0; } int select_fd_timeout_rw(int fd, int sec, int usec) { struct timeval ptv; fd_set fdset; int ret; ptv.tv_sec = sec; ptv.tv_usec = usec; FD_ZERO(&fdset); FD_SET(fd, &fdset); ret = select(fd + 1, &fdset, &fdset, NULL, &ptv); if(ret < 0){ return -1; }else if(ret > 0){ return 1; } return 0; } /* 在阻塞模式下, send函數的過程是將應用程序請求發送的數據拷貝到發送緩存中發送就返回.但由於發送緩存的存在, 表現為:如果發送緩存大小比請求發送的大小要大,那麼send函數立即返回,同時向網絡中發送數據; 否則,send會等待接收端對之前發送數據的確認,以便騰出緩存空間容納新的待發送數據, 再返回(接收端協議棧只要將數據收到接收緩存中,就會確認,並不一定要等待應用程序調用recv),如果一直沒有空間能容納待發送的數據,則一直阻塞; 在非阻塞模式下,send函數的過程僅僅是將數據拷貝到協議棧的緩存區而已,如果緩存區可用空間不夠,則盡能力的拷貝,立即返回成功拷貝的大小;如緩存區可用空間為0,則返回-1,同時設置errno為EAGAIN. 阻塞就是干不完不准回來,非阻塞就是你先干,我現看看有其他事沒有,完了告訴我一聲。 */ int CloseSocket(int sockfd) { shutdown(sockfd, 2); close(sockfd); sockfd = INVALID_SOCKET; return (0); } int Writen(int sckid, unsigned char *buf, int len, int sec, int usec) { struct timeval ptv = {0,0}; ptv.tv_sec = sec; ptv.tv_usec = usec; //設置發送超時 setsockopt(sckid, SOL_SOCKET,SO_SNDTIMEO, (char *)&ptv,sizeof(struct timeval)); if (send(sckid, buf, len, 0) != len) { return (-1); } return (len); } int Readn(int sckid, char *buf, int len, int sec, int usec) { int rc = 0; struct timeval ptv = {0,0}; ptv.tv_sec = sec; ptv.tv_usec = usec; //設置接收超時 setsockopt(sckid, SOL_SOCKET,SO_RCVTIMEO, (char *)&ptv,sizeof(struct timeval)); if ((rc = recv(sckid, buf, len, 0)) <= 0) { return (-1); } return (rc); } int ConnectRemote(char *ip, int port, int sec, int usec) { struct sockaddr_in psckadd; struct linger Linger; int sckcli; int on = 1; struct timeval ptv; memset((char *)(&psckadd), '0', sizeof(struct sockaddr_in)); psckadd.sin_family = AF_INET; psckadd.sin_addr.s_addr = inet_addr(ip); psckadd.sin_port = htons((u_short) port); if ((sckcli = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { return (INVALID_SOCKET); } //設置連接超時時間 ptv.tv_sec = sec; ptv.tv_usec = usec; if (setsockopt(sckcli, SOL_SOCKET, SO_SNDTIMEO, &ptv, sizeof(ptv))) { close(sckcli); return (INVALID_SOCKET); } if (connect(sckcli, (struct sockaddr *)(&psckadd), sizeof(struct sockaddr_in)) < 0) { close(sckcli); return (INVALID_SOCKET); } //設置接收tcp外帶數據 if (setsockopt(sckcli, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on))) { CloseSocket(sckcli); return (INVALID_SOCKET); } on = 1; setsockopt(sckcli, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); /* TCP_NODELAY和TCP_CORK都是禁用Nagle算法,只不過NODELAY完全關閉算法,立即發送而TCP_CORK完全由自己決定發送時機 */ setsockopt(sckcli, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)); return (sckcli); } int ListenRemote(int port) { int yes=1; int server_socket = INVALID_SOCKET; struct sockaddr_in server_addr; //創建套接字 if((server_socket=socket(AF_INET,SOCK_STREAM,0))<0){ return INVALID_SOCKET; } //設置為可重復使用 if(setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))==-1){ close(server_socket); return INVALID_SOCKET; } //設置服務器地址信息設置 server_addr.sin_family=AF_INET; //TCP server_addr.sin_port=htons(port); server_addr.sin_addr.s_addr=INADDR_ANY; //本地IP地址 memset(server_addr.sin_zero, 0,sizeof(server_addr.sin_zero)); //綁定套接字與地址信息 if(bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1){ close(server_socket); return INVALID_SOCKET; } //偵聽 if(listen(server_socket,5)==-1){ close(server_socket); return INVALID_SOCKET; } return server_socket; } int AcceptRemote(int sockfd) { int client_sockfd = INVALID_SOCKET;//客戶端套接字 struct sockaddr_in remote_addr; //客戶端網絡地址結構體 int sin_size=sizeof(struct sockaddr_in); /*等待客戶端連接請求到達*/ if((client_sockfd=accept(sockfd,(struct sockaddr *)&remote_addr,&sin_size))<0) { return INVALID_SOCKET; } //默認為阻塞模式 return client_sockfd; } void setOpetinNoBlock(int fd) { //設置為非阻塞模式 int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); } /* int socket_recv(int sockfd, char* buffer, int buflen) { int rs=1; int ret; while(rs) { ret = recv(activeevents[i].data.fd, buffer, buflen, 0); if(ret < 0) { // 由於是非阻塞的模式,所以當errno為EAGAIN時,表示當前緩沖區已無數據可讀 // 在這裡就當作是該次事件已處理處. if(errno == EAGAIN) break; } else if(ret == 0) { // 這裡表示對端的socket已正常關閉. break; } if(buflen == ret) rs = 1; // 需要再次讀取 else rs = 0; } return ret; } int socket_send(int sockfd, const char* buffer, int buflen) { int tmp; int total = buflen; const char *p = buffer; while(1) { tmp = send(sockfd, p, total, 0); if(tmp < 0) { // 當send收到信號時,可以繼續寫,但這裡返回-1. if(errno == EINTR) return -1; // 當socket是非阻塞時,如返回此錯誤,表示寫緩沖隊列已滿, // 在這裡做延時後再重試. if(errno == EAGAIN) { usleep(1000); continue; } return -1; } if((size_t)tmp == total) return buflen; total -= tmp; p += tmp; } return tmp; } */
tcp.h
#define INVALID_SOCKET -1
int select_fd_init(fd_set *fdset);
int select_fd_del(fd_set *fdset, int fd);
int select_fd_add(fd_set *fdset, int fd);
int select_fd_check_r(fd_set *fdset_r, int fd, int sec, int usec);
int select_fd_check_w(fd_set *fdset_w, int fd, int sec, int usec);
int select_fd_check_rw(fd_set *fdset_r, fd_set *fdset_w,int fd, int sec, int usec);
int select_fd_check2(fd_set *fdset, int fd);
int select_fd_timeout_r(int fd, int sec, int usec);
int select_fd_timeout_w(int fd, int sec, int usec);
int select_fd_timeout_rw(int fd, int sec, int usec);
int CloseSocket(int sockfd);
int Writen(int sckid, unsigned char *buf, int len, int sec, int usec);
int Readn(int sckid, char *buf, int len, int sec, int usec);
int ConnectRemote(char *ip, int port, int sec, int usec);
int ListenRemote(int port);
int AcceptRemote(int sockfd);
void setOpetinNoBlock(int fd);
server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "tcp.h"
void* work(void* arg)
{
int sckcli = *(int*) arg;
char buff[1024];
int ret;
ret = Readn( sckcli, buff, 1024, 3, 0);
if(ret < 0){
printf("read error\n");
close(sckcli);
return NULL;
}else{
buff[ret]='\0';
printf("received:%s\n", buff);
}
ret = Writen(sckcli, buff, ret, 3, 0);
if(ret < 0){
printf("write error\n");
}
close(sckcli);
}
void* accpet_work(void* arg)
{
int sckcli;
int sckser = *(int *)arg;
pthread_t tid;
pthread_attr_t attr;
fd_set fd;
int ret;
while(1)
{
ret = select_fd_timeout_r(sckser, 3, 0);
if(ret <= 0){
printf("1AcceptRemote ret socket Accept錯誤errno = [%d] %d\n", errno, ret);
continue;
}
sckcli = AcceptRemote(sckser);
if(INVALID_SOCKET == sckcli)
{
printf("2AcceptRemote ret socket Accept錯誤errno = [%d]\n", errno);
usleep(1);
continue;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, work, &sckcli);
}
}
/*
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, THREAD_FUNCTION, arg);
*/
int main(int rgvs, char **rgva)
{
int ret;
int len;
int sckser1;
int sckser2;
int i=0;
char buff[1024];
if(rgvs <3){
printf("%s \n", rgva[0]);
return 0;
}
pthread_t tid1;
pthread_t tid2;
pthread_attr_t attr;
sckser1 = ListenRemote(atoi(rgva[1]));
if(sckser1 >0){
setOpetinNoBlock(sckser1);
pthread_create(&tid1, NULL, accpet_work, &sckser1);
}
sckser2 = ListenRemote(atoi(rgva[2]));
if(sckser2 > 0){
setOpetinNoBlock(sckser2);
pthread_create(&tid2, NULL, accpet_work, &sckser2);
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
close(sckser1);
close(sckser2);
}
client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "tcp.h" int main(int rgvs, char **rgva) { int ret; int len; int sckcli; int i=0; char buff[1024]; if(rgvs <4){ printf("%s <destip> <destport> <conect time>\n", rgva[0]); return 0; } for(i=0;i<atoi(rgva[3]); i++) { sckcli = ConnectRemote(rgva[1], atoi(rgva[2]), 10, 0); if(INVALID_SOCKET == sckcli) { printf("%s conect %s %s error \n", rgva[0], rgva[1], rgva[2]); return 0; } sprintf(buff, "%d",i); printf("Enter string to send: %s\n", buff); len=Writen(sckcli,buff,strlen(buff),10, 0); len=Readn(sckcli,buff,BUFSIZ,10, 0); if(len < 0){ printf("read error\n"); }else{ buff[len]='\0'; printf("received:%s\n", buff); } close(sckcli); } return 0; }