服務器端:服務器
/* 文件:server.c PS:第一個鏈接上服務器的客戶端,稱爲client1,第二個鏈接上服務器的客戶端稱爲client2 這個服務器的功能是: 1:對於client1,它返回"first",並在client2鏈接上以後,將client2通過轉換後的IP和port發給client1; 2:對於client2,它返回client1通過轉換後的IP和port和自身的port,並在隨後斷開與他們的鏈接。 */ #include <stdio.h> #include <unistd.h> #include <signal.h> #include <sys/socket.h> #include <fcntl.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <arpa/inet.h> #define MAXLINE 128 #define SERV_PORT 8877 //發生了致命錯誤,退出程序 void error_quit(const char *str) { fprintf(stderr, "%s", str); //若是設置了錯誤號,就輸入出錯緣由 if( errno != 0 ) fprintf(stderr, " : %s", strerror(errno)); printf("\n"); exit(1); } int main(void) { int i, res, cur_port; int connfd, firstfd, listenfd; int count = 0; char str_ip[MAXLINE]; //緩存IP地址 char cur_inf[MAXLINE]; //當前的鏈接信息[IP+port] char first_inf[MAXLINE]; //第一個連接的信息[IP+port] char buffer[MAXLINE]; //臨時發送緩衝區 socklen_t clilen; struct sockaddr_in cliaddr; struct sockaddr_in servaddr; //建立用於監聽TCP協議套接字 listenfd = socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); //把socket和socket地址結構聯繫起來 res = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); if( -1 == res ) error_quit("bind error"); //開始監聽端口 res = listen(listenfd, INADDR_ANY); if( -1 == res ) error_quit("listen error"); while( 1 ) { //接收來自客戶端的鏈接 connfd = accept(listenfd,(struct sockaddr *)&cliaddr, &clilen); if( -1 == connfd ) error_quit("accept error"); inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, str_ip, sizeof(str_ip)); count++; //對於第一個連接,將其的IP+port存儲到first_inf中, //並和它創建長連接,而後向它發送字符串'first', if( count == 1 ) { firstfd = connfd; cur_port = ntohs(cliaddr.sin_port); snprintf(first_inf, MAXLINE, "%s %d", str_ip, cur_port); strcpy(cur_inf, "first\n"); write(connfd, cur_inf, strlen(cur_inf)+1); } //對於第二個連接,將其的IP+port發送給第一個連接, //將第一個連接的信息和他自身的port返回給它本身, //而後斷開兩個連接,並重置計數器 else if( count == 2 ) { cur_port = ntohs(cliaddr.sin_port); snprintf(cur_inf, MAXLINE, "%s %d\n", str_ip, cur_port); snprintf(buffer, MAXLINE, "%s %d\n", first_inf, cur_port); write(connfd, buffer, strlen(buffer)+1); write(firstfd, cur_inf, strlen(cur_inf)+1); close(connfd); close(firstfd); count = 0; } //若是程序運行到這裏,那確定是出錯了 else error_quit("Bad required"); } return 0; }
客戶端:
/* 文件:client.c PS:第一個鏈接上服務器的客戶端,稱爲client1,第二個鏈接上服務器的客戶端稱爲client2 這個程序的功能是:先鏈接上服務器,根據服務器的返回決定它是client1仍是client2, 如果client1,它就從服務器上獲得client2的IP和Port,鏈接上client2, 如果client2,它就從服務器上獲得client1的IP和Port和自身經轉換後的port, 在嘗試鏈接了一下client1後(這個操做會失敗),而後根據服務器返回的port進行監聽。 這樣之後,就能在兩個客戶端之間進行點對點通訊了。 */ #include <stdio.h> #include <unistd.h> #include <signal.h> #include <sys/socket.h> #include <fcntl.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <arpa/inet.h> #define MAXLINE 128 #define SERV_PORT 8877 typedef struct { char ip[32]; int port; }server; //發生了致命錯誤,退出程序 void error_quit(const char *str) { fprintf(stderr, "%s", str); //若是設置了錯誤號,就輸入出錯緣由 if( errno != 0 ) fprintf(stderr, " : %s", strerror(errno)); printf("\n"); exit(1); } int main(int argc, char **argv) { int i, res, port; int connfd, sockfd, listenfd; unsigned int value = 1; char buffer[MAXLINE]; socklen_t clilen; struct sockaddr_in servaddr, sockaddr, connaddr; server other; if( argc != 2 ) error_quit("Using: ./client <IP Address>"); //建立用於連接(主服務器)的套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); sockaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1], &sockaddr.sin_addr); //設置端口能夠被重用 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); //鏈接主服務器 res = connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if( res < 0 ) error_quit("connect error"); //從主服務器中讀取出信息 res = read(sockfd, buffer, MAXLINE); if( res < 0 ) error_quit("read error"); printf("Get: %s", buffer); //若服務器返回的是first,則證實是第一個客戶端 if( 'f' == buffer[0] ) { //從服務器中讀取第二個客戶端的IP+port res = read(sockfd, buffer, MAXLINE); sscanf(buffer, "%s %d", other.ip, &other.port); printf("ff: %s %d\n", other.ip, other.port); //建立用於的套接字 connfd = socket(AF_INET, SOCK_STREAM, 0); memset(&connaddr, 0, sizeof(connaddr)); connaddr.sin_family = AF_INET; connaddr.sin_addr.s_addr = htonl(INADDR_ANY); connaddr.sin_port = htons(other.port); inet_pton(AF_INET, other.ip, &connaddr.sin_addr); //嘗試去鏈接第二個客戶端,前幾回可能會失敗,由於穿透還沒成功, //若是鏈接10次都失敗,就證實穿透失敗了(多是硬件不支持) while( 1 ) { static int j = 1; res = connect(connfd, (struct sockaddr *)&connaddr, sizeof(connaddr)); if( res == -1 ) { if( j >= 10 ) error_quit("can't connect to the other client\n"); printf("connect error, try again. %d\n", j++); sleep(1); } else break; } strcpy(buffer, "Hello, world\n"); //鏈接成功後,每隔一秒鐘向對方(客戶端2)發送一句hello, world while( 1 ) { res = write(connfd, buffer, strlen(buffer)+1); if( res <= 0 ) error_quit("write error"); printf("send message: %s", buffer); sleep(1); } } //第二個客戶端的行爲 else { //從主服務器返回的信息中取出客戶端1的IP+port和本身公網映射後的port sscanf(buffer, "%s %d %d", other.ip, &other.port, &port); //建立用於TCP協議的套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&connaddr, 0, sizeof(connaddr)); connaddr.sin_family = AF_INET; connaddr.sin_addr.s_addr = htonl(INADDR_ANY); connaddr.sin_port = htons(other.port); inet_pton(AF_INET, other.ip, &connaddr.sin_addr); //設置端口重用 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); //嘗試鏈接客戶端1,確定會失敗,但它會在路由器上留下記錄, //以幫忙客戶端1成功穿透,鏈接上本身 res = connect(sockfd, (struct sockaddr *)&connaddr, sizeof(connaddr)); if( res < 0 ) printf("connect error\n"); //建立用於監聽的套接字 listenfd = socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); //設置端口重用 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); //把socket和socket地址結構聯繫起來 res = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); if( -1 == res ) error_quit("bind error"); //開始監聽端口 res = listen(listenfd, INADDR_ANY); if( -1 == res ) error_quit("listen error"); while( 1 ) { //接收來自客戶端1的鏈接 connfd = accept(listenfd,(struct sockaddr *)&sockaddr, &clilen); if( -1 == connfd ) error_quit("accept error"); while( 1 ) { //循環讀取來自於客戶端1的信息 res = read(connfd, buffer, MAXLINE); if( res <= 0 ) error_quit("read error"); printf("recv message: %s", buffer); } close(connfd); } } return 0; }
出處:http://blog.csdn.net/small_qch/article/details/8815028網絡
我的注:併發
我認爲,service的做用遠不止這些,service能夠作一些驗證連通性、數據校驗等等的事情,只有當A和B真正開始通訊了,這時才考慮斷開A、B與service的連接。socket