TCP/IP协议(面向连接协议)类似于打电话时,对方一定在手机附近并且此刻都在和对方进行通话。一定保证双方都在线,才能进行数据传输。UDP/IP协议(无连接协议)就像邮箱,不保证对方一定在等你邮件且对方不在你也可以给对方发送数据。实际上TCP协议、UDP协议,还有重要的TCP协议中的三次握手(建立连接)和四次挥手(关闭连接)等在网上也都解释得非常详细了,所以我就不多说了。
Server端程序代码:
/* * 服务器端 Server.c * */ #include <winsock2.h> #include <stdio.h> #include <string.h> #define BUFFSIZE 1024 int main(int argc, char**argv) { int Ret; WSADATA wsaData; SOCKET ListeningSocket; SOCKET NewConnection; SOCKADDR_IN ServerAddr; SOCKADDR_IN ClientAddr; int ClientAddrLen = sizeof(ClientAddr); unsigned short Port = 5150; char sendData[BUFFSIZE]; char recvData[BUFFSIZE]; if((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0) { printf("WSASTARTUP_ERROR: %d ", Ret); return 0; } //创建一个套接字来监听客户机连接 if((ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { printf("SOCKET_ERROR: %d ", INVALID_SOCKET); return 0; } /* * 填充SOCKADDR_IN结构,这个结构将告知bind我们想要在5150端口监听所有接口上的连接 */ ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(Port); //将端口变量从主机字节顺序转换位网络字节顺序 ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用bind将这个地址信息和套接字绑定起来 if(bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR) { printf("BIND_ERROR: %d ", SOCKET_ERROR); return 0; } //监听客户机连接。这里使用5个backlog if(listen(ListeningSocket, 5) == SOCKET_ERROR) { printf("LISTEN_ERROR: %d ", SOCKET_ERROR); return 0; } //连接到达时,接受连接 printf("正在接受连接..."); if((NewConnection = accept(ListeningSocket, (SOCKADDR *)&ClientAddr, &ClientAddrLen)) == INVALID_SOCKET) { printf("ACCPET_ERROR: %d ", INVALID_SOCKET); closesocket(ListeningSocket); return 0; } printf("检测到一个连接: %s 端口:%d ", inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port)); //聊天 while(true) { //接收数据 Ret = recv(NewConnection, recvData, BUFFSIZE, 0); if(Ret > 0) printf("C.C.: %s ", recvData); else if(Ret < 0) printf("RECV_ERROR: %d ", SOCKET_ERROR); else { printf("对方退出程序,聊天结束!"); break; } //发送数据 printf(" 鲁鲁:"); scanf("%s", sendData); if(strcmp(sendData, "quit") == 0) //退出 break; if(send(NewConnection, sendData, BUFFSIZE, 0) == SOCKET_ERROR) { printf("消息发送失败! "); break; } } //从容关闭 shutdown(NewConnection, SD_BOTH); //完成新接受的连接后,用closesocket API关闭这些套接字 closesocket(NewConnection); closesocket(ListeningSocket); //应用程序完成对接的处理后,调用WSACleanup if(WSACleanup() == SOCKET_ERROR) { printf("WSACLEANUP_ERROR: %d ", WSAGetLastError()); return 0; } system("pause"); return 0; }
Client端程序代码:
/* * 客户端 Client.c * */ #include <winsock2.h> #include <stdio.h> #include <string.h> #define BUFFSIZE 1024 int main(int argc, char**argv) { int Ret; WSADATA wsaData; SOCKET s; SOCKADDR_IN ServerAddr; unsigned short Port = 5150; char sendData[BUFFSIZE]; char recvData[BUFFSIZE]; if((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0) { printf("WSASTARTUP_ERROR: %d ", Ret); return 0; } if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { printf("SOCKET_ERROR: %d ", INVALID_SOCKET); return 0; } ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(Port); ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.101");// 这里S_un.S_addr在不同的IDE中可能不一样,然后IPv4地址使用该程序所运行在的PC上的IPv4地址 if((connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr))) == SOCKET_ERROR) { printf("CONNECT_ERROR: %d ", SOCKET_ERROR); closesocket(s); return 0; } //Chat while(true) { printf(" C.C:"); scanf("%s", sendData); if(strcmp(sendData, "quit") == 0) //quit break; if(send(s, sendData, BUFFSIZE, 0) == SOCKET_ERROR) { printf("消息发送失败! "); break; } Ret = recv(s, recvData, BUFFSIZE, 0); if(Ret > 0) printf("鲁鲁: %s ", recvData); else if(Ret < 0) printf("RECV_ERROR: %d ", SOCKET_ERROR); else { printf("对方退出程序,聊天结束!"); break; } } shutdown(s, SD_BOTH); closesocket(s); if(WSACleanup() == SOCKET_ERROR) { printf("WSACLEANUP_ERROR: %d ", WSAGetLastError()); return 0; } system("pause"); return 0; }
运行结果:
对于所有可能出现的错误我都加上了会出错的函数名前缀,以便于定位出现错误的位置。
其他的书上解释得很详细,就不暴露自己水平了233...
p.s.2018-05-13 15:10:46
AF_INET的原型是:
#define AF_INET 2
然后关于客户端代码中的inet_addr()可看一下我的这篇,其中有一段相关解释。