您的位置:首页 > 理论基础 > 计算机网络

基于TCP/IP的迭代回声服务器/客户端

2017-04-17 12:11 501 查看

服务器/客户端的实现顺序:

                              


需要注意的是:客户端只有等到服务器调用listen函数后才能调connect函数。同时要清楚,客户端调用connect函数前,服务器端有可能率先调用accept函数。当然,此时服务器在调用accept函数时进入阻塞状态,直到客户端调用connect函数为止。

迭代服务器/客户端:

插入循环语句反复调用accept函数,图示如下

                                 


简单的迭代服务器/客户端代码:

服务器

//服务器代码
#include<stdlib.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib") //显示调用ws2_32.lib

void ErrorHanding(char* message);
int main() {
WSADATA wsadata;
SOCKET hserverSocket, hclientSocket;
SOCKADDR_IN serveraddr, clientaddr;
int szClientAdd,str_len;
char message[4096] = "\0";

if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) //初始化winsock库
ErrorHanding("WSAStartup() error");
hserverSocket = socket(PF_INET, SOCK_STREAM, 0); // IPV4 面向连接传输
if (hserverSocket == INVALID_SOCKET)
ErrorHanding("socket() error");

memset(&serveraddr, 0, sizeof(serveraddr));//清空,值全为0

serveraddr.sin_family = AF_INET;    //IPv4
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);  //自动寻找本机地址
serveraddr.sin_port = htons(6666);  //端口为 6666

//绑定服务器地址
if (bind(hserverSocket, (SOCKADDR*)&serveraddr, sizeof(serveraddr)) == SOCKET_ERROR)
ErrorHanding("bind() error");
printf("服务器启动成功.........\n");

//使套接字成为服务器套接字
if (listen(hserverSocket,5) == SOCKET_ERROR) {  //等待队列的最大数目为5
ErrorHanding("listen() error");
}
szClientAdd = sizeof(clientaddr);
for (int i = 0; i < 5; i++) {
hclientSocket = accept(hserverSocket, (SOCKADDR*)&clientaddr, &szClientAdd);
if (hclientSocket == INVALID_SOCKET)
ErrorHanding("accept() Error");
printf("这是客户端:   NO . %d\n", i + 1);

closesocket(hclientSocket);  //关闭客户套接字
}
getchar();
closesocket(hserverSocket);  //关闭服务器套接字
WSACleanup();  //清理winsock库
return 0;
}
void ErrorHanding(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
}

客户端

#include<stdlib.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
void ErrorHanding(char* message);
int main() {
WSADATA wsadata;
SOCKET hclientsocket;
SOCKADDR_IN servAddr;

char message[30];
int strlen;

if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
ErrorHanding("WSAStartup() error");
hclientsocket = socket(PF_INET, SOCK_STREAM, 0);
if (hclientsocket == INVALID_SOCKET)
ErrorHanding("socket() error");

memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //回送地址,访问本地
servAddr.sin_port = htons(6666); //端口号

printf("客户端启动成功.......\n");
//客户端套接字通过目的地址建立连接
if (connect(hclientsocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
ErrorHanding("conect() error");
getchar();
closesocket(hclientsocket);
WSACleanup();
return 0;
}
void ErrorHanding(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
}


每次客户端打开,获取任意字符后结束,服务器都能接收到消息,但是只能相应5次,获取任意字符后就关闭了,当然这只是为了了解原理而写,非常简单,不能识别同一客户端,

迭代回声服务器/客户端:

程序的基本运行方式如下

1)服务器在同一时刻只与一个客户端相连,并提供回声服务。

2)服务器依次向5个客户端提供服务并退出

3)客户端接受用户输入的字符串并发送到服务器端

4)服务器端将接收的自负出汗数据传回客户端,即“回声”

5)服务器端与客户端之间的字符串回声一直执行到客户端输入Q为止

服务器:

//服务器代码
#include<stdlib.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib") //显示调用ws2_32.lib

void ErrorHanding(char* message);
int main() {
WSADATA wsadata;
SOCKET hserverSocket, hclientSocket;
SOCKADDR_IN serveraddr, clientaddr;
int szClientAdd,str_len;
char message[4096] = "\0";

if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) //初始化winsock库
ErrorHanding("WSAStartup() error");
hserverSocket = socket(PF_INET, SOCK_STREAM, 0); // IPV4 面向连接传输
if (hserverSocket == INVALID_SOCKET)
ErrorHanding("socket() error");

memset(&serveraddr, 0, sizeof(serveraddr));//清空,值全为0

serveraddr.sin_family = AF_INET;    //IPv4
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);  //自动寻找本机地址
serveraddr.sin_port = htons(6666);  //端口为 6666

//绑定服务器地址
if (bind(hserverSocket, (SOCKADDR*)&serveraddr, sizeof(serveraddr)) == SOCKET_ERROR)
ErrorHanding("bind() error");
printf("服务器启动成功.........\n");

//使套接字成为服务器套接字
if (listen(hserverSocket,5) == SOCKET_ERROR) {  //等待队列的最大数目为5
ErrorHanding("listen() error");
}
szClientAdd = sizeof(clientaddr);
for (int i = 0; i < 5; i++) {
hclientSocket = accept(hserverSocket, (SOCKADDR*)&clientaddr, &szClientAdd);
if (hclientSocket == INVALID_SOCKET)
ErrorHanding("accept() Error");
printf("这是客户端:   NO . %d\n", i + 1);
while ((str_len = recv(hclientSocket, message, sizeof(message), 0)) != 0) {
printf("%s", message);
if (send(hclientSocket, message, str_len, 0) == SOCKET_ERROR) {
ErrorHanding("send() error");
break;
}
memset(message, '\0', sizeof(message));
}
closesocket(hclientSocket);  //关闭客户套接字
}
getchar();
closesocket(hserverSocket);  //关闭服务器套接字
WSACleanup();  //清理winsock库
return 0;
}
void ErrorHanding(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
}

客户端:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
void ErrorHanding(char* message);
#define bufsize 100
int main() {
WSADATA wsadata;
SOCKET hclientsocket;
SOCKADDR_IN servAddr;

char message[bufsize];
int str_len=0;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
ErrorHanding("WSAStartup() error");
hclientsocket = socket(PF_INET, SOCK_STREAM, 0);
if (hclientsocket == INVALID_SOCKET)
ErrorHanding("socket() error");

memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //回送地址,访问本地
servAddr.sin_port = h
bfe0
tons(6666); //端口号

printf("客户端启动成功.......\n");
//客户端套接字通过目的地址建立连接
if (connect(hclientsocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
ErrorHanding("conect() error");
while (1) {
printf("输入Q退出:");
fgets(message, bufsize, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) break;
send(hclientsocket, message, strlen(message), 0);
memset(message, '\0', sizeof(message));
str_len = recv(hclientsocket, message, bufsize - 1, 0);
if (str_len == SOCKET_ERROR) ErrorHanding("recv() error\n");
printf("服务器消息:%s", message);
}
closesocket(hclientsocket);
WSACleanup();
return 0;
}
void ErrorHanding(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
}

上面客户端代码存在一点问题,TCP传输的数据不存在数据边界,如果传输数据大,经过多个路由器的转发后,recv函数执行第一次收到的字符串长度会小于实际发送的长度。

改进的客户端代码:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
void ErrorHanding(char* message);
#define bufsize 2048
int main() {
WSADATA wsadata;
SOCKET hclientsocket;
SOCKADDR_IN servAddr;

char message[bufsize]="\0";
int str_len=0,rec_cnt,rec_len;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
ErrorHanding("WSAStartup() error");
hclientsocket = socket(PF_INET, SOCK_STREAM, 0);
if (hclientsocket == INVALID_SOCKET)
ErrorHanding("socket() error");

memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //回送地址,访问本地
servAddr.sin_port = htons(6666); //端口号

printf("客户端启动成功.......\n");
//客户端套接字通过目的地址建立连接
if (connect(hclientsocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
ErrorHanding("conect() error");
while (1) {
printf("输入Q退出:");
fgets(message, bufsize, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) break;
str_len = send(hclientsocket, message, strlen(message), 0);
memset(message, '\0', sizeof(message));
//保证数据传输的完整性
rec_len = 0;
while (rec_len < str_len) {
rec_cnt = recv(hclientsocket, &message[rec_len], bufsize - 1, 0);
if (rec_cnt == SOCKET_ERROR) ErrorHanding("recv() error\n");
rec_len += rec_cnt;
}
/*str_len = recv(hclientsocket, message, bufsize - 1, 0);
if (str_len == SOCKET_ERROR) ErrorHanding("recv() error\n");*/
printf("服务器消息:%s", message);
}
closesocket(hclientsocket);
WSACleanup();
return 0;
}
void ErrorHanding(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息