UNIX网络编程简单练习

来源:互联网 发布:轻兵器杂志 知乎 编辑:程序博客网 时间:2024/06/06 02:37

以下是我电某专业课作业,都比较简单,我就简单记录,写的也比较简陋。

一、

客户端:从命令行读入服务器的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;}elsebreak;}}
服务端的代码如下:

// 服务器端程序#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 1int 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 2048int 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;}elsebreak;}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 2048void 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 2048int 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;}elsebreak;}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 2048void 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 2048int 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;}elsebreak;}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 2048typedef 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){// 释放结构中的vectorvector<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;}}}}
程序的执行截图如下图所示,可完成题中给出的要求。






0 0
原创粉丝点击