以下是我电某专业课作业,都比较简单,我就简单记录,写的也比较简陋。
一、
客户端:从命令行读入服务器的IP地址;并连接到服务器;
服务器端:接收客户的连接请求,并显示客户的IP地址和端口号;
客户端的代码如下:
// 客户端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int sockfd;
const char* ip_string = "127.0.0.1";
const int port = 65531;
int main(){
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1){
printf("%s\n", "套接字创建失败");
exit(-1);
}
struct sockaddr_in srv_addr;
socklen_t addrlen;
addrlen = sizeof(srv_addr);
memset(&srv_addr, 0, addrlen);
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
srv_addr.sin_port = htons(port);
while (1){
if (connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))
== -1)
{
printf("connect error!\n");
sleep(1);
continue;
}
else
break;
}
}
服务端的代码如下:
// 服务器端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define PORT 65531
#define BACKLOG 1
int main(){
int listenfd, connectfd;
struct sockaddr_in server, client;
socklen_t sin_size;
if((listenfd=socket(AF_INET, SOCK_STREAM, 0)) == -1){
fprintf(stderr, "%s\n", "监听套接字创建失败。");
exit(-1);
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1){
fprintf(stderr, "%s\n", "bind执行失败。");
exit(-1);
}
if (listen(listenfd, BACKLOG) == -1){
fprintf(stderr, "%s\n", "监听错误");
exit(-1);
}
sin_size = sizeof(client);
while(1){
if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &sin_size)) == -1){
fprintf(stderr, "%s\n", "accept错误");
exit(-1);
}
printf("%s\n", "一个客户端连接:");
char *ip = inet_ntoa(client.sin_addr);
int port = ntohs(client.sin_port);
printf("%s%s\n", "客户的IP是:", ip);
printf("%s%d;\n", "客户的端口号是:", port);
}
}
程序执行的效果如下图所示,先执行server,再执行client。
二
客户端:
从命令行读入服务器的IP地址;并连接到服务器;
循环从命令行读入一行字符串,并传递给服务器,由服务器对字符串反转,并将结果返回客户程序;
客户程序显示反转后的字符串;
服务器端:
接收客户的连接请求,并显示客户的IP地址和端口号;
接收客户传来的字符串,反转后传递给客户;
客户端的代码如下:
// 客户端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define STRSIZE 2048
int sockfd;
const char* ip_string = "127.0.0.1";
const int port = 65531;
int main(){
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1){
printf("%s\n", "套接字创建失败");
exit(-1);
}
struct sockaddr_in srv_addr;
socklen_t addrlen;
addrlen = sizeof(srv_addr);
memset(&srv_addr, 0, addrlen);
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
srv_addr.sin_port = htons(port);
while (1){
if (connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))
== -1)
{
printf("connect error!\n");
sleep(1);
continue;
}
else
break;
}
char in_string[2048];
int len = 0;
while(1){
printf("%s\n", "请输入一个字符串:");
scanf("%s", in_string);
if(send(sockfd, in_string, strlen(in_string), 0) == -1){
printf("%s\n", "发送失败");
sleep(1);
continue;
}
len = recv(sockfd, (void *)in_string, STRSIZE, 0);
if (len == -1){
printf("%s\n", "接收数据失败");
exit(-1);
}
else{
in_string[len] = '\0';
}
printf("%s\n", in_string);
}
}
服务端的代码如下:
// 服务器端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define PORT 65531
#define BACKLOG 1
#define STRSIZE 2048
void revstring(char * str, int len){
int start = 0;
int end = len - 1;
while(start != end && start <= end){
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
int main(){
int listenfd, connectfd;
struct sockaddr_in server, client;
socklen_t sin_size;
if((listenfd=socket(AF_INET, SOCK_STREAM, 0)) == -1){
fprintf(stderr, "%s\n", "监听套接字创建失败。");
exit(-1);
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1){
fprintf(stderr, "%s\n", "bind执行失败。");
exit(-1);
}
if (listen(listenfd, BACKLOG) == -1){
fprintf(stderr, "%s\n", "监听错误");
exit(-1);
}
sin_size = sizeof(client);
char rev_str[STRSIZE];
int len = 0;
while(1){
if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &sin_size)) == -1){
fprintf(stderr, "%s\n", "accept错误");
exit(-1);
}
printf("%s\n", "一个客户端连接:");
char *ip = inet_ntoa(client.sin_addr);
int port = ntohs(client.sin_port);
printf("%s%s\n", "客户的IP是:", ip);
printf("%s%d;\n", "客户的端口号是:", port);
while(1){
len = recv(connectfd, rev_str, STRSIZE, 0);
if (len == -1 || len == 0){
printf("%s\n", "接收数据失败");
exit(-1);
}
rev_str[len] = '\0';
if (strcmp(rev_str, "exit") == 0){
close(connectfd);
continue;
}
printf("收到的数据是:%s\n", rev_str);
revstring(rev_str, strlen(rev_str));
printf("反转后的数据是:%s\n", rev_str);
send(connectfd, rev_str, len, 0);
}
}
}
程序执行的截图如下图所示:
以上两个程序都比较简单,没有涉及服务器的并发,就是简单的练习一下。
三、
利用多线程技术实现如下并发网络程序,要求对上课时的实现进行完善,利用线程专用数据TSD实现。服务器和客户程序分别实现如下功能:
1、服务器等待客户连接,连接成功后显示客户地址,接着接收该客户的名字并显示,然后接收来自客户的信息(字符串),将该字符串反转,并将结果送回客户。要求服务器具有同时处理多个客户的能力。当某个客户断开连接时,打印所有该客户输入的数据。
2、客户首先与服务器连接,接着接收用户输入客户的名字,将该名字发送给服务器,然后接收用户输入的字符串,并发送给服务器,然后接收服务器返回的经处理后的字符串,并显示之。当用户输入Ctrl+D,终止连接并退出。
当用户输入Ctrl+D时,相当于EOF,所以在代码中当scanf等于EOF时,就close连接,并exit退出进程。
客户代码如下所示:
// 客户端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define STRSIZE 2048
int sockfd;
const char* ip_string = "127.0.0.1";
const int port = 65531;
int main(){
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1){
printf("%s\n", "套接字创建失败");
exit(-1);
}
struct sockaddr_in srv_addr;
socklen_t addrlen;
addrlen = sizeof(srv_addr);
memset(&srv_addr, 0, addrlen);
srv_addr.sin_family = AF_INET;
char * server_addr = (char*) malloc(100);
printf("%s\n", "请输入服务器端的地址:");
scanf("%s", server_addr);
srv_addr.sin_addr.s_addr = inet_addr(server_addr);
srv_addr.sin_port = htons(port);
while (1){
if (connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))
== -1)
{
printf("connect error!\n");
sleep(1);
continue;
}
else
break;
}
char in_string[2048];
// 输入客户的名字
printf("%s\n", "请输入客户的名字!");
scanf("%s", in_string);
if(send(sockfd, in_string, strlen(in_string), 0) == -1){
printf("%s\n", "发送客户名字失败!");
exit(-1);
}
// 客户输入字符串
int len = 0;
while(1){
printf("%s\n", "请输入一个字符串:");
if (scanf("%s", in_string) == EOF){
close(sockfd);
exit(0);
}
if(send(sockfd, in_string, strlen(in_string), 0) == -1){
printf("%s\n", "发送失败");
sleep(1);
continue;
}
len = recv(sockfd, (void *)in_string, STRSIZE, 0);
if (len == -1){
printf("%s\n", "接收数据失败");
exit(-1);
}
else{
in_string[len] = '\0';
}
printf("%s\n", in_string);
}
}
在服务端中,利用线程技术来实现并发,在接收到客户的连接后,将sockaddr_in结构和连接套接字组成的结构作为参数传递给线程。在线程中接收和打印用户的信息。
revstring用来反转字符串,thr_fun是线程的执行函数。在thr_fun中,首先利用sockaddr_in结构打印客户端的ip和port信息,并将客户输入的信息存放在vector容器中,在客户退出时,即recv调用返回值小于等于0时,遍历vector容器,然后打印出用户输入的所有信息。
服务端的代码如下:
// 服务器端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <iostream>
#include <pthread.h>
using namespace std;
#define PORT 65531
#define BACKLOG 1
#define STRSIZE 2048
void revstring(char * str, int len){
int start = 0;
int end = len - 1;
char temp;
while(start != end && start <= end){
temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
// 需要传递给线程的变量
struct info_pthread{
int connectfd;
struct sockaddr_in client;
};
void * thr_fun(void *arg){
struct info_pthread *struct_ptr = (struct info_pthread *)arg;
vector<char*> fromClient;
// 服务端打印客户的ip和端口信息
printf("%s\n", "一个客户端连接:");
char *ip = inet_ntoa((struct_ptr->client).sin_addr);
int port = ntohs((struct_ptr->client).sin_port);
printf("%s%s\n", "客户的IP是:", ip);
printf("%s%d;\n", "客户的端口号是:", port);
int connectfd = struct_ptr -> connectfd;
char* str_rev;
int len = 0;
// 打印客户的名称
str_rev = (char*)malloc(STRSIZE * sizeof(char));
len = recv(connectfd, str_rev, STRSIZE, 0);
if (len <= 0){
printf("%s\n", "接收客户名称的数据失败");
exit(-1);
}
*(str_rev + len) = '\0';
printf("客户的名称是:%s\n", str_rev);
fromClient.push_back(str_rev);
// 循环接收客户的信息
char * str_rev_cp;
while(1){
str_rev = (char*)malloc(STRSIZE * sizeof(char));
len = recv(connectfd, str_rev, STRSIZE, 0);
if (len <= 0){
// 循环输出客户输入过的信息
cout << "客户连接断开,客户输入过的信息如下:" << endl;
for (vector<char*>::iterator it = fromClient.begin(); it != fromClient.end(); ++it){
cout << *it << endl;
free(*it);
}
free(struct_ptr);
return NULL;
}
*(str_rev + len)= '\0';
printf("收到来自%s的数据是:%s\n", fromClient[0], str_rev);
fromClient.push_back(str_rev);
str_rev_cp = (char*)malloc(sizeof(char) * strlen(str_rev) + 1);
strcpy(str_rev_cp, str_rev);
revstring(str_rev_cp, strlen(str_rev));
printf("反转后发送给%s的数据是:%s\n", fromClient[0], str_rev_cp);
send(connectfd, str_rev_cp, len, 0);
free(str_rev_cp);
}
}
int main(){
int listenfd, connectfd;
struct sockaddr_in server, client;
socklen_t sin_size;
if((listenfd=socket(AF_INET, SOCK_STREAM, 0)) == -1){
fprintf(stderr, "%s\n", "监听套接字创建失败。");
exit(-1);
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1){
fprintf(stderr, "%s\n", "bind执行失败。");
exit(-1);
}
if (listen(listenfd, BACKLOG) == -1){
fprintf(stderr, "%s\n", "监听错误");
exit(-1);
}
sin_size = sizeof(client);
char rev_str[STRSIZE];
int len = 0;
int err;
while(1){
if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &sin_size)) == -1){
fprintf(stderr, "%s\n", "accept错误");
exit(-1);
}
pthread_t tid;
struct info_pthread * struct_ptr = (struct info_pthread *)malloc(sizeof(struct info_pthread));
struct_ptr->client = client;
struct_ptr->connectfd = connectfd;
err = pthread_create(&tid, NULL, thr_fun, (void *)struct_ptr);
if (err != 0){
cout << "线程创建错误,错误代码:" << err << endl;
exit(1);
}
}
}
程序执行的截图如下图所示,首先开启服务端,再开启客户端。
这里开启两个客户端程序,并如下图所示输入必要的信息和测试数据,服务端会收到来自客户端的信息,并将它反转后发送给客户。在客户端退出时,会将客户输入的数据全部输出。
四
服务器利用I/O复用技术,实现同时向多个客户提供服务。要求:服务器:接收客户连接请求,并打印客户IP地址及端口号,然后接收客户发来的字符串,并打印该字符串和其来自与哪个客户。同时向客户返回该字符串。当某一客户断开连接时,要求服务器打印该客户输入的所有字符。
客户:从命令行接收服务器地址,并向服务器发起连接请求,连接成功后,从标准输入接收字符串并发送给服务器,等待服务器响应并打印接收的信息。
客户端的代码比较简单,如下所示:
// 客户端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define STRSIZE 2048
int sockfd;
const char* ip_string = "127.0.0.1";
const int port = 65531;
int main(){
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1){
printf("%s\n", "套接字创建失败");
exit(-1);
}
struct sockaddr_in srv_addr;
socklen_t addrlen;
addrlen = sizeof(srv_addr);
memset(&srv_addr, 0, addrlen);
srv_addr.sin_family = AF_INET;
char * server_addr = (char*) malloc(100);
printf("%s\n", "请输入服务器端的地址:");
scanf("%s", server_addr);
srv_addr.sin_addr.s_addr = inet_addr(server_addr);
srv_addr.sin_port = htons(port);
while (1){
if (connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))
== -1)
{
printf("connect error!\n");
sleep(1);
continue;
}
else
break;
}
char in_string[2048];
// 客户输入字符串
int len = 0;
while(1){
printf("%s\n", "请输入一个字符串:");
if (scanf("%s", in_string) == EOF){
close(sockfd);
exit(0);
}
if(send(sockfd, in_string, strlen(in_string), 0) == -1){
printf("%s\n", "发送失败");
sleep(1);
continue;
}
len = recv(sockfd, (void *)in_string, STRSIZE, 0);
if (len == -1){
printf("%s\n", "接收数据失败");
exit(-1);
}
else{
in_string[len] = '\0';
}
printf("%s\n", in_string);
}
}
服务端的代码如下所示:
服务端主要是用到了select系统调用来实现同时向多个用户提供服务的。
程序中使用了CLIENT结构来存储关于连接客户的一些信息,包括ip,端口,连接套接字fd,地址结构,并在CLIENT结构中使用vector容器来存放用户输入过的信息。
print_err函数封装了对出错的处理。
Close函数封装了关闭连接套接字时需要做的处理,包括打印客户连接的信息,释放客户结构CLIENT中包含的指针等。
handle_recv_msg封装了对接收到客户连接请求的处理,它通过push_back向CLIENT结构的vector容器里压入用户输入的数据,并将收到的信息再传回给客户端。
main函数首先定义了变量,然后创建监听套接字,绑定端口,再进入while循环,通过select调用来判断各个文件描述符是否可读。若可读,则select调用返回,然后再判断监听套接字是否在可读的套接字集中,如果在,则调用accept函数,接受客户,并做一定的处理;循环每个client的fd,判断它们是否是可读的连接套接字,如果是则接收它们的信息,然后调用handle_recv_msg函数来处理。
// 服务器端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <iostream>
#include <sys/select.h>
using namespace std;
#define PORT 65531
#define BACKLOG 1
#define STRSIZE 2048
typedef struct CLIENT{
int fd;
char *ip;
struct sockaddr_in addr;
int port;
char *name;
vector<char*> str_vec;
} CLIENT;
void print_client_addr(CLIENT *client){
// 服务端打印客户的ip和端口信息
printf("%s\n", "一个客户端连接:");
char *ip = inet_ntoa((client->addr).sin_addr);
int port = ntohs((client->addr).sin_port);
printf("%s%s\n", "客户的IP是:", ip);
printf("%s%d;\n", "客户的端口号是:", port);
client->port = port;
client->ip = ip;
}
void print_err(string err_info){
cout << err_info << endl;
exit(0);
}
void Close(CLIENT *client){
// 释放结构中的vector
vector<char*>::iterator it;
cout << "客户("<< client->ip <<")的连接断开,客户输入过的信息如下:" << endl;
for (vector<char*>::iterator it = (client->str_vec).begin(); it != (client->str_vec).end(); ++it){
cout << *it << endl;
free(*it);
}
free(client->name);
close(client->fd);
}
void handle_recv_msg(CLIENT *client, char *msg){
char* m_msg = (char *)malloc(sizeof(char) * STRSIZE);
strcpy(m_msg, msg);
cout << "收到来自("<< (*client).ip << ":" << client->port << ")的消息:"<< msg <<endl;
(client->str_vec).push_back(m_msg);
send(client->fd, m_msg, strlen(m_msg), 0);
}
int main(){
int listenfd, connectfd, sockfd, maxfd, i, maxi, nready, len;
struct sockaddr_in server;
socklen_t sin_size;
fd_set readset, allset;
CLIENT client[FD_SETSIZE];
char recvbuf[STRSIZE];
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
fprintf(stderr, "%s\n", "监听套接字创建失败。");
exit(-1);
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1){
fprintf(stderr, "%s\n", "bind执行失败。");
exit(-1);
}
if (listen(listenfd, BACKLOG) == -1){
print_err("监听错误");
}
maxfd = listenfd;
sin_size = sizeof(server);
maxi = -1;
for (int i = 0; i < FD_SETSIZE; ++i){
client[i].fd = -1;
}
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
while(1){
struct sockaddr_in addr;
readset = allset;
nready = select(maxfd + 1, &readset, NULL, NULL, NULL);
if (nready == -1){
if (errno == EINTR) // 被信号中断后恢复
continue;
print_err("select 系统调用出错,进程退出");
}
if (FD_ISSET(listenfd, &readset)){
if ((connectfd = accept(listenfd, (struct sockaddr*)&addr, &sin_size)) == -1){
print_err("accept错误");
}
for(i = 0; i < FD_SETSIZE; ++i){
if (client[i].fd < 0){
client[i].fd = connectfd;
client[i].name = (char *)malloc(sizeof(char) * STRSIZE);
client[i].addr = addr;
client[i].name[0] = '\0';
break;
}
}
if (i == FD_SETSIZE){
printf("%s\n", "已接收较多的客户端连接!");
}
FD_SET(client[i].fd, &allset);
print_client_addr(&client[i]);
if (connectfd > maxfd)
maxfd = connectfd;
if (i > maxi)
maxi = i;
--nready;
if (nready <= 0)
continue;
}
for (i = 0; i <= maxi; ++i){
if ((sockfd = client[i].fd) < 0)
continue;
if (FD_ISSET(sockfd, &readset)){
len = recv(sockfd, recvbuf, STRSIZE, 0);
if (len <= 0)
{
Close(&client[i]);
FD_CLR(sockfd, &allset);
client[i].fd = -1;
}
else{
handle_recv_msg(&client[i], recvbuf);
}
if (--nready <= 0)
break;
}
}
}
}
程序的执行截图如下图所示,可完成题中给出的要求。