服务端是linux下面跑的,可以稍等改下就是windows版本了
server.c
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <arpa/inet.h> #include <netinet/in.h> typedef struct sockaddr SA; typedef struct sockaddr_in SA_IN;//服务器网络地址结构体 //sockaddr,sockaddr_in二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别。 //sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息。是一种通用的套接字地址。 //而sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作。 //使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了 typedef struct { struct in_addr ip; int port; }Client; /* 记录ip与端口 */ int main(int argc, char **argv) { SA_IN server;//服务器网络地址结构体 int sockfd; Client clients[2]; char s; socklen_t addrlen = sizeof(SA_IN); bzero(&server,sizeof(SA_IN)); //初始化 server.sin_port = htons(8877); //服务器端口号 server.sin_family = AF_INET; //设置为IP通信 server.sin_addr.s_addr = INADDR_ANY; //允许连接到所有本地地址上 /*创建服务器端套接字--IPv4协议,面向无连接通信,SOCK_DGRAM是UDP协议*/ sockfd = socket(AF_INET,SOCK_DGRAM,0); if(sockfd < 0) { perror("server_sockfd creation failed"); return -1; } if(bind(sockfd,(SA *)&server,sizeof(SA_IN)) < 0) { perror("server socket bind failed"); return -1; } while(1) { bzero(clients, sizeof(Client) * 2); /* 接收两个心跳包并记录其与此连接的ip与端口 */ printf("waiting for a packet...\n"); //阻塞读包recvfrom recvfrom(sockfd,&s,sizeof(char),0,(SA *) &server,&addrlen); //读取一个字节 memcpy(&clients[0].ip,&server.sin_addr,sizeof(struct in_addr)); //拷贝第一个连接信息 clients[0].port = server.sin_port; //赋值端口 printf("%s\t%d OK\n", inet_ntoa(clients[0].ip), ntohs(clients[0].port)); //输出IP地址和端口 printf("waiting for a packet...\n"); //阻塞读包recvfrom recvfrom(sockfd,&s,sizeof(char),0,(SA *)&server,&addrlen); //继续读取 memcpy(&clients[1].ip,&server.sin_addr,sizeof(struct in_addr)); //拷贝第二个连接信息 clients[1].port = server.sin_port; //赋值端口 printf( "%s\t%d OK\n", inet_ntoa(clients[1].ip),ntohs(clients[1].port)); //输出IP地址和端口 /* 分别向两台机器发送与此服务连接的ip与端口 */ server.sin_addr = clients[0].ip; server.sin_port = clients[0].port; sendto(sockfd,&clients[1],sizeof(Client),0,(SA *)&server,sizeof(SA_IN)); server.sin_addr = clients[1].ip; server.sin_port = clients[1].port; sendto(sockfd,&clients[0],sizeof(Client),0,(SA *)&server,sizeof(SA_IN)); } return(0); }
client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <pthread.h> #define SER "*.*.*.*" #define PORT 8877 typedef struct { struct in_addr ip; int port; }IP; typedef struct sockaddr SA; typedef struct sockaddr_in SA_IN; typedef struct { int sockfd; SA *addr; int len; }Conn,*LPConn; void echo_cli( int sockfd, SA *addr, socklen_t *len ) { char buf[1024]; while (1) { memset(buf,0,sizeof(buf)); printf( ">" ); gets(buf); sendto(sockfd,buf,strlen(buf),0,addr,sizeof(SA_IN)); if(strcmp(buf,"exit") == 0) break; } } static void* receive_data(void* lpData) { LPConn lpCon = (LPConn)lpData; char buf[1024]; while(1) { memset(buf,0,sizeof(buf)); recvfrom(lpCon->sockfd,buf,sizeof(buf)-1,0,lpCon->addr,&lpCon->len); buf[strlen( buf ) - 1] = '\0'; printf("%s\n",buf); if(strcmp(buf,"exit") == 0) break; } free(lpData); return NULL; } int main( int argc, char **argv ) { SA_IN server; IP ip; socklen_t addrlen = sizeof(SA_IN); char s = 'a'; int sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); if(sockfd < 0) { perror("client_sockfd creation failed"); return -1; } bzero(&server, sizeof(SA_IN)); server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(SER); server.sin_port = htons(PORT); sendto(sockfd,&s,sizeof(char),0,(SA *)&server,sizeof(SA_IN)); recvfrom(sockfd,&ip,sizeof(IP),0,(SA *)&server,&addrlen); printf( "IP:%s\tPort:%d\n", inet_ntoa(ip.ip), ntohs(ip.port)); server.sin_addr = ip.ip; server.sin_port = ip.port; LPConn lpCon = (LPConn)malloc(sizeof(Conn)); lpCon->addr = (SA *)&server; lpCon->len = addrlen; lpCon->sockfd = sockfd; pthread_t tidp; /* 创建线程pthread */ if ((pthread_create(&tidp, NULL, receive_data, (void*)lpCon)) == -1) { printf("create thread error!\n"); return 1; } echo_cli( sockfd, (SA *) &server, &addrlen ); /* 等待线程pthread释放 */ if (pthread_join(tidp, NULL)) { printf("thread is not exit...\n"); return -2; } close(sockfd); return 0; }
改了一个windows的客户端测试程序
#include "stdafx.h" #include <stdio.h> #include <winsock2.h> #include <Windows.h> #pragma comment(lib,"ws2_32.lib") #define SERVER "*.*.*.*" #define PORT 8877 typedef struct { struct in_addr ip; int port; }Client; /* ip与端口 */ typedef struct sockaddr SA; typedef struct sockaddr_in SA_IN; typedef struct { int sockfd; SA *addr; int len; }Conn,*LPConn; void echo_cli( int sockfd, SA *addr, int *len ) { char buf[1024]; while (true) { memset(buf,0,sizeof(buf)); printf(">"); gets_s(buf,sizeof(buf)); sendto(sockfd,buf,strlen(buf),0,addr,sizeof(SA_IN)); if (strcmp( buf,"exit") == 0) break; } } int receive_data(void* lpData) { LPConn lpCon = (LPConn)lpData; char buf[1024]; while(true) { memset(buf,0,sizeof(buf)); recvfrom(lpCon->sockfd,buf,sizeof(buf)-1,0,lpCon->addr,&lpCon->len); printf( "%s", buf ); buf[strlen( buf ) - 1] = '\0'; } delete lpCon; return 0; } int _tmain(int argc, _TCHAR* argv[]) { //初始化网络环境 WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { printf("WSAStartup failed\n"); return -1; } int sockfd; SA_IN server; Client client; int addrlen = sizeof(SA_IN); char s = 'a'; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd == INVALID_SOCKET) { printf("create socket failed\n"); return -1; } memset(&server,0,sizeof(SA_IN)); server.sin_family = AF_INET; server.sin_addr.S_un.S_addr = inet_addr(SERVER); server.sin_port = htons(PORT); int i = sendto(sockfd,&s,1,0,(SA*)&server,sizeof(SA_IN)); char* pBuf = new char[sizeof(Client)]; recvfrom(sockfd,pBuf,sizeof(Client),0,(SA*)&server,&addrlen); memcpy(&client,pBuf,sizeof(Client)); printf("向 ip:%s\tPort:%d发数据\n", inet_ntoa(client.ip),ntohs(client.port)); server.sin_addr = client.ip; server.sin_port = client.port; LPConn lpCon = new Conn; lpCon->addr = (SA *)&server; lpCon->len = addrlen; lpCon->sockfd = sockfd; //启动接收线程 DWORD threadId; HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)receive_data,lpCon,0,&threadId); CloseHandle(hThread); //启动输入方法 echo_cli(sockfd,(SA*)&server,&addrlen); //清理网络环境 WSACleanup(); system("pause"); return 0; }
有些光猫不知道为啥跑不通,可能是因为NAT的4种类型不完全支持。下面再贴出转过来的4种类型
1. NAT的原理与类型
NAT是IETF标准,它通过将局域网内的主机IP地址映射为Internet上有效的公网IP地址,从而实现了网络地址的复用。使用NAT技术,局域网内的多台PC可以共享单个、全局路由的IP地址,减少了所需的IP地址的数量。
NAT主要可以分为两类:基本NAT和NAPT ( Network Address Port Translation )。
基本NAT一般是用于NAT勇夺多个公网IP的情形下,将公网IP地址与内网主机进行静态绑定,基本上这种类型的NAT设备已经很少了。或许根本我们就没机会见到。
NAPT(Network Address/Port Translators):其实这种才是我们常说的 NAT。NAPT将内部连接映射到外部网络中的一个单独IP地址上,同时在该地址上加上一个由NAT设备选定的端口号。根据映射方式不同,NAPT可以分为圆锥型NAT和对称性NAT,其中圆锥型NAT包括完全圆锥型NAT、地址限制圆锥型NAT和端口限制圆锥型NAT。
1.1完全圆锥型NAT( Full Cone NAT )
完全圆锥型NAT,将从一个内部IP地址和端口来的所有请求,都映射到相同的外部IP地址和端口。并且,任何外部主机通过向映射的外部地址发送报文,都可以实现和内部主机进行通信。这是一种比较宽松的策略,只要建立了内部网络的IP地址和端口与公网IP地址和端口的映射关系,所有的Internet上的主机都可以访问该NAT之后的主机。
1.2地址限制圆锥型NAT( Address Restricted Cone NAT )
地址限制圆锥型NAT也是将从相同的内部IP地址和端口来的所有请求映射到相同的公网IP地址和端口。但是与完全圆锥型NAT不同,当且仅当内部主机之前已经向公网主机发送过报文,此时公网主机才能向内网主机发送报文。
1.3端口限制圆锥型NAT( Port Restricted Cone NAT )
类似与地址限制圆锥型NAT,但是更严格。端口受限圆锥型NAT增加了端口号的限制,当前仅当内网主机之前已经向公网主机发送了报文,公网主机才能和此内网主机通信。
1.4对称型NAT( Symmetric NAT)
对称型NAT把从同一内网地址和端口到相同目的地址和端口的所有请求,都映射到同一个公网地址和端口。如果同一个内网主机,用相同的内网地址和端口向另外一个目的地址发送报文,则会用不同的映射。这和端口限制型NAT不同,端口限制型NAT是所有请求映射到相同的公网IP地址和端口,而对称型NAT是不同的请求有不同的映射。
- 文章2302
- 用户1336
- 访客10970171
清明节邀请我们以静思与敬意祭奠祖先。