InputStream input = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(input));
while(true){
br.readLine();
}
其中,Sender是一个实现了Runnable接口的类,用它来专门负责发送数据,主线程只需要不听地接受数据就行。Sender类中的run()方法应该具有以下代码:
PrintWriter pw = new PrintWriter(socket.getOutputStream());
while(true){
pw.println(数据);
}
即使按上面的方式做了,程序还是有问题,因为它只能在一个时间内为一个客户服务,不能同时为多个客户服务,如多要想同时为多个客户服务,服务器应具有类似以下的代码:
ServerSocker ss = new ServerSocker(端口号);
socket = null;
while(true){
socket = ss.accept();
Thread thread1 = new Thread(new Sender());
thread1.start();
Thread thread2 = new Thread(new Receiver());
thread2.start();
}
在这里,新开启了2个线程分别负责接收和发送。Receiver是一个与Sender非常相似的类,它主要用来接收数据。在客户端,我们同样应开启2个线程:
Socket socket = new Socket(服务器IP,端口号);
Sender sender = new Sender(socket);
Thread thread1 = new Thread(sender);
thread1.start();
Receiver receiver = new Receiver(socket);
Thread thread2 = new Thread(receiver);
thread2.start();
我们来亲自动手实现案例
实现客户端
/* 头文件 */ #include <stdio.h> #include "winsock2.h" /* 常量 */ #define RECV_BUFFER_SIZE 8192 /************************************* * main * 功能 socket通信客户端 **************************************/ void main(int argc, char* argv[]) { // 变量定义 SOCKADDR_IN clientService;// 地址 SOCKET ConnectSocket;// socket WSADATA wsaData;// 库 LPVOID recvbuf;// 接收缓存 int bytesSent; int bytesRecv = 0; char sendbuf[32] = "get information";// 默认发送的数据 // 初始化socket库, 保存ws2_32.dll已经加载 int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != NO_ERROR) printf("Error at WSAStartup()\n"); // 创建socket ConnectSocket = socket(AF_INET, // IPv4 SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信 IPPROTO_TCP// 使用TCP协议 ); if (ConnectSocket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); WSACleanup(); return; } // 设置服务端的通信协议、IP地址、端口 clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" ); clientService.sin_port = htons( 10000 ); // 连接到服务端 if ( connect( ConnectSocket, // socket (SOCKADDR*) &clientService, // 地址 sizeof(clientService) // 地址的大小 ) == SOCKET_ERROR) { printf( "Failed to connect(%d)\n",WSAGetLastError() ); WSACleanup(); return; } // 准备发送数据 // 如果输入参数是-d,那么发送的数据是“download file”否则是"get information" if(argc ==2 && (!lstrcmp(argv[1], "-d"))) { lstrcpyn(sendbuf, "download file", 32); } // 向服务端发送数据 bytesSent = send( ConnectSocket, // socket sendbuf,// 发送的数据 lstrlen(sendbuf)+1,// 数据长度 0 );// 无标志 if(bytesSent == SOCKET_ERROR) { printf( "send error (%d)\n", WSAGetLastError()); closesocket(ConnectSocket); return; } printf( "Bytes Sent: %ld\n", bytesSent ); // 准备接收数据 recvbuf = HeapAlloc(GetProcessHeap(), 0, RECV_BUFFER_SIZE); // 循环接收 while( bytesRecv != SOCKET_ERROR ) { //Sleep(50); bytesRecv = recv( ConnectSocket, // socket recvbuf, // 接收数据缓存 RECV_BUFFER_SIZE,// 缓存大小 0 );// 无标志 if ( bytesRecv == 0 ) { printf( "Connection Closed.\n"); break; } // TODO,处理接收的数据,这里只简单的将收到的数据大小显示 printf( "Bytes Recv: %ld\n", bytesRecv ); } HeapFree(GetProcessHeap(), 0, recvbuf); WSACleanup(); return; }
实现服务端
/* 头文件 */ #include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> /* 常量 */ #define DEFAULT_PORT "10000" // 端口 #define MAX_REQUEST 1024 // 接收数据的缓存大小 #define BUF_SIZE 4096 // 发送数据的缓存大小 /************************************* * CommunicationThread * 功能 用于接收和发送数据的线程 * 为每一个连接的客户端创建一个接收发送数据的线程, * 可以使用多个客户端同时连接到服务端 * 参数 lpParameter,SOKCET **************************************/ DWORD WINAPI CommunicationThread( LPVOID lpParameter ) { DWORD dwTid = GetCurrentThreadId(); // 获得参数sokcet SOCKET socket = (SOCKET)lpParameter; // 为接收数据分配空间 LPSTR szRequest = HeapAlloc(GetProcessHeap(),0, MAX_REQUEST); int iResult; int bytesSent;// 用于保存send的返回值,实际发送的数据的大小 // 接收数据 iResult = recv(socket, // socket szRequest, // 接收缓存 MAX_REQUEST, // 缓存大小 0);// 标志 if (iResult == 0)// 接收数据失败,连接已经关闭 { printf("Connection closing...\n"); HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 1; } else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误 { printf("recv failed: %d\n", WSAGetLastError()); HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 1; } else if (iResult > 0) // 接收数据成功 { // 显示接收到的数据 printf("\tCommunicationThread(%d)\tBytes received: %d\n", dwTid, iResult); printf("\tCommunicationThread(%d)\trequest string is (%s)\n",dwTid, szRequest); // 如果接收到的数据是"download file" if (lstrcmpi(szRequest, "download file") == 0) { // 读取文件download.txt将发送 HANDLE hFile; LPVOID lpReadBuf; // 发送缓存 DWORD dwBytesRead; DWORD dwFileSize; DWORD dwSendFile = 0; hFile = CreateFile("download.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("\tCommunicationThread\tCould not open file (error %d)\n", GetLastError()); send(socket, "error", 6, 0); closesocket(socket); return 1; } // 分配发送数据缓存 lpReadBuf = HeapAlloc(GetProcessHeap(), 0 , BUF_SIZE); // 获取文件大小 dwFileSize = GetFileSize(hFile, NULL); // 循环发送 while(1) { // 读文件到缓存 if(!ReadFile(hFile, lpReadBuf, BUF_SIZE, &dwBytesRead, NULL)) { printf("\tCommunicationThread\tCould not read from file (error %d)\n", GetLastError()); closesocket(socket); CloseHandle(hFile); return 1; } // 发送读取的文件数据 bytesSent = send(socket, lpReadBuf, dwBytesRead, 0); if( bytesSent == SOCKET_ERROR) { printf("\tCommunicationThread\tsend error %d\n", WSAGetLastError()); closesocket(socket); CloseHandle(hFile); return 1; } // 显示发送数据的大小 printf("\tCommunicationThread(%d)\tsend %d bytes\n", dwTid, bytesSent); // 累加,已经发送的大小 dwSendFile += dwBytesRead; // 如果所有文件数据都已经发送 if(dwSendFile == dwFileSize) { printf("\tCommunicationThread\tFile download ok\n"); break;// 退出循环 } } // 释放内存、关闭连接,关闭文件 HeapFree(GetProcessHeap(), 0 , lpReadBuf); CloseHandle(hFile); closesocket(socket); } // 如果接收到的数据是"get information" else if (lstrcmpi(szRequest, "get information") == 0) { // 发送数据 bytesSent = send(socket, // socket "this is information", // 数据 lstrlen("this is information")+1, // 数据长度 0);// 标志 // 判断是否成功 if( bytesSent == SOCKET_ERROR) { printf("\tCommunicationThread\tsend error %d\n", WSAGetLastError()); closesocket(socket); return 1; } printf("\tCommunicationThread(%d)\tsend %d bytes\n",dwTid, bytesSent); } else// 收到未知数据 { printf ("unreferenced request\n"); } } // 释放接收数据缓存,关闭socket HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 0; } /************************************* * int __cdecl main(void) * 功能 socket服务端 **************************************/ int __cdecl main(void) { WSADATA wsaData; SOCKET ListenSocket = INVALID_SOCKET;// 监听socket SOCKET ClientSocket = INVALID_SOCKET;// 连接socket struct addrinfo *result = NULL, hints; int iResult;// 保存返回结果 // 初始化Winsock,保证Ws2_32.dll已经加载 iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } // 地址 ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; // 获取主机地址,保证网络协议可用等 iResult = getaddrinfo(NULL, // 本机 DEFAULT_PORT, // 端口 &hints, // 使用的网络协议,连接类型等 &result);// 结果 if ( iResult != 0 ) { printf("getaddrinfo failed: %d\n", iResult); WSACleanup(); return 1; } // 创建socket,用于监听 ListenSocket = socket( result->ai_family, // 网络协议,AF_INET,IPv4 result->ai_socktype, // 类型,SOCK_STREAM result->ai_protocol);// 通信协议,TCP if (ListenSocket == INVALID_SOCKET) { printf("socket failed: %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } // 绑定到端口 iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen); if (iResult == SOCKET_ERROR) { printf("bind failed: %d\n", WSAGetLastError()); freeaddrinfo(result); closesocket(ListenSocket); WSACleanup(); return 1; } printf("bind\n"); freeaddrinfo(result);// reuslt不再使用 // 开始监听 iResult = listen(ListenSocket, SOMAXCONN); printf("start listen......\n"); if (iResult == SOCKET_ERROR) { printf("listen failed: %d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } while (1) { // 接收客户端的连接,accept函数会等待,直到连接建立 printf("ready to accept\n"); ClientSocket = accept(ListenSocket, NULL, NULL); // accept函数返回,说明已经有客户端连接 // 返回连接socket printf("accept a connetion\n"); if (ClientSocket == INVALID_SOCKET) { printf("accept failed: %d\n", WSAGetLastError()); closesocket(ListenSocket); break;// 等待连接错误,退出循环 } // 为每一个连接创建一个数据发送的接收线程, // 使服务端又可以立即接收其他客户端的连接 if(!CreateThread( NULL, 0, CommunicationThread, // 线程函数 (LPVOID)ClientSocket, // 将socket作为参数 0, NULL)) { printf("Create Thread error (%d)", GetLastError()); break; } } // 循环退出,释放DLL。 WSACleanup(); return 0; }
/* 头文件 */ #include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> /* 常量 */ #define DEFAULT_PORT "10000" // 端口 #define MAX_REQUEST 1024 // 接收数据的缓存大小 #define BUF_SIZE 4096 // 发送数据的缓存大小 /************************************* * CommunicationThread * 功能 用于接收和发送数据的线程 * 为每一个连接的客户端创建一个接收发送数据的线程, * 可以使用多个客户端同时连接到服务端 * 参数 lpParameter,SOKCET **************************************/ DWORD WINAPI CommunicationThread( LPVOID lpParameter ) { DWORD dwTid = GetCurrentThreadId(); // 获得参数sokcet SOCKET socket = (SOCKET)lpParameter; // 为接收数据分配空间 LPSTR szRequest = HeapAlloc(GetProcessHeap(),0, MAX_REQUEST); int iResult; int bytesSent;// 用于保存send的返回值,实际发送的数据的大小 // 接收数据 iResult = recv(socket, // socket szRequest, // 接收缓存 MAX_REQUEST, // 缓存大小 0);// 标志 if (iResult == 0)// 接收数据失败,连接已经关闭 { printf("Connection closing...\n"); HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 1; } else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误 { printf("recv failed: %d\n", WSAGetLastError()); HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 1; } else if (iResult > 0) // 接收数据成功 { // 显示接收到的数据 printf("\tCommunicationThread(%d)\tBytes received: %d\n", dwTid, iResult); printf("\tCommunicationThread(%d)\trequest string is (%s)\n",dwTid, szRequest); // 如果接收到的数据是"download file" if (lstrcmpi(szRequest, "download file") == 0) { // 读取文件download.txt将发送 HANDLE hFile; LPVOID lpReadBuf; // 发送缓存 DWORD dwBytesRead; DWORD dwFileSize; DWORD dwSendFile = 0; hFile = CreateFile("download.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("\tCommunicationThread\tCould not open file (error %d)\n", GetLastError()); send(socket, "error", 6, 0); closesocket(socket); return 1; } // 分配发送数据缓存 lpReadBuf = HeapAlloc(GetProcessHeap(), 0 , BUF_SIZE); // 获取文件大小 dwFileSize = GetFileSize(hFile, NULL); // 循环发送 while(1) { // 读文件到缓存 if(!ReadFile(hFile, lpReadBuf, BUF_SIZE, &dwBytesRead, NULL)) { printf("\tCommunicationThread\tCould not read from file (error %d)\n", GetLastError()); closesocket(socket); CloseHandle(hFile); return 1; } // 发送读取的文件数据 bytesSent = send(socket, lpReadBuf, dwBytesRead, 0); if( bytesSent == SOCKET_ERROR) { printf("\tCommunicationThread\tsend error %d\n", WSAGetLastError()); closesocket(socket); CloseHandle(hFile); return 1; } // 显示发送数据的大小 printf("\tCommunicationThread(%d)\tsend %d bytes\n", dwTid, bytesSent); // 累加,已经发送的大小 dwSendFile += dwBytesRead; // 如果所有文件数据都已经发送 if(dwSendFile == dwFileSize) { printf("\tCommunicationThread\tFile download ok\n"); break;// 退出循环 } } // 释放内存、关闭连接,关闭文件 HeapFree(GetProcessHeap(), 0 , lpReadBuf); CloseHandle(hFile); closesocket(socket); } // 如果接收到的数据是"get information" else if (lstrcmpi(szRequest, "get information") == 0) { // 发送数据 bytesSent = send(socket, // socket "this is information", // 数据 lstrlen("this is information")+1, // 数据长度 0);// 标志 // 判断是否成功 if( bytesSent == SOCKET_ERROR) { printf("\tCommunicationThread\tsend error %d\n", WSAGetLastError()); closesocket(socket); return 1; } printf("\tCommunicationThread(%d)\tsend %d bytes\n",dwTid, bytesSent); } else// 收到未知数据 { printf ("unreferenced request\n"); } } // 释放接收数据缓存,关闭socket HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 0; } /************************************* * int __cdecl main(void) * 功能 socket服务端 **************************************/ int __cdecl main(void) { WSADATA wsaData; SOCKET ListenSocket = INVALID_SOCKET;// 监听socket SOCKET ClientSocket = INVALID_SOCKET;// 连接socket struct addrinfo *result = NULL, hints; int iResult;// 保存返回结果 // 初始化Winsock,保证Ws2_32.dll已经加载 iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } // 地址 ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; // 获取主机地址,保证网络协议可用等 iResult = getaddrinfo(NULL, // 本机 DEFAULT_PORT, // 端口 &hints, // 使用的网络协议,连接类型等 &result);// 结果 if ( iResult != 0 ) { printf("getaddrinfo failed: %d\n", iResult); WSACleanup(); return 1; } // 创建socket,用于监听 ListenSocket = socket( result->ai_family, // 网络协议,AF_INET,IPv4 result->ai_socktype, // 类型,SOCK_STREAM result->ai_protocol);// 通信协议,TCP if (ListenSocket == INVALID_SOCKET) { printf("socket failed: %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } // 绑定到端口 iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen); if (iResult == SOCKET_ERROR) { printf("bind failed: %d\n", WSAGetLastError()); freeaddrinfo(result); closesocket(ListenSocket); WSACleanup(); return 1; } printf("bind\n"); freeaddrinfo(result);// reuslt不再使用 // 开始监听 iResult = listen(ListenSocket, SOMAXCONN); printf("start listen......\n"); if (iResult == SOCKET_ERROR) { printf("listen failed: %d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } while (1) { // 接收客户端的连接,accept函数会等待,直到连接建立 printf("ready to accept\n"); ClientSocket = accept(ListenSocket, NULL, NULL); // accept函数返回,说明已经有客户端连接 // 返回连接socket printf("accept a connetion\n"); if (ClientSocket == INVALID_SOCKET) { printf("accept failed: %d\n", WSAGetLastError()); closesocket(ListenSocket); break;// 等待连接错误,退出循环 } // 为每一个连接创建一个数据发送的接收线程, // 使服务端又可以立即接收其他客户端的连接 if(!CreateThread( NULL, 0, CommunicationThread, // 线程函数 (LPVOID)ClientSocket, // 将socket作为参数 0, NULL)) { printf("Create Thread error (%d)", GetLastError()); break; } } // 循环退出,释放DLL。 WSACleanup(); return 0; }