网络编程 TCP

本文探讨了如何修改服务器端代码以实现多线程模式,以提高并发能力,并提供了客户端连接和数据交互的完整示例。通过实例展示了如何在C语言中使用线程和套接字处理TCP连接,包括客户端的IP查找、连接请求及服务器端的接收和处理。
摘要由CSDN通过智能技术生成

查看代码服务端

#include <stdlib.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <unistd.h>
#include <arpa/inet.h> 
 
#define portnumber 3333
 
int main(int argc, char* argv[]) {
	int local_listen_socket, server_session_socket;
	struct sockaddr_in server_addr_info_struct;
	struct sockaddr_in client_addr_info_struct;
	int size_of_sockaddr_in;
	int read_got_bytes_nr;
	char buffer[1024];
 
 
	/* socket: 服务器端开始建立sockfd描述符 */
	if ((local_listen_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // AF_INET i.e. IPV4; SOCK_STREAM i.e. TCP
		fprintf(stderr, "Socket error:%s\n\a", strerror(errno));
		exit(1);
	}
 
	/* 准备 sockaddr结构及其内部IP、端口信息 */
	bzero(&server_addr_info_struct, sizeof(struct sockaddr_in)); // 初始化,置0
	server_addr_info_struct.sin_family = AF_INET;                 // Internet
	server_addr_info_struct.sin_addr.s_addr = htonl(INADDR_ANY);  // 将本机host上的long数据转化为网络上的long数据,使服务器程序能运行在不同CPU的主机上 
													// INADDR_ANY 表示主机监听任意/所有IP地址。
	//server_addr_info_struct.sin_addr.s_addr=inet_addr("192.168.1.1");  //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
	server_addr_info_struct.sin_port = htons(portnumber);         // (将本机器上的short数据转化为网络上的short数据)端口号
 
	/* bind: 绑定sockfd描述符 和 IP、端口 */
	if (bind(local_listen_socket, (struct sockaddr*)(&server_addr_info_struct), sizeof(struct sockaddr)) == -1) {
		fprintf(stderr, "ERR bind():%s\n\a", strerror(errno));
		exit(1);
	}
 
	/* 设置允许连接的最大客户端数 */
	if (listen(local_listen_socket, 5) == -1) {
		fprintf(stderr, "ERR listen():%s\n\a", strerror(errno));
		exit(1);
	}
 
	while (1) {
		size_of_sockaddr_in = sizeof(struct sockaddr_in);
		fprintf(stderr, "Listening & Accepting...\n");
		if ((server_session_socket = accept(local_listen_socket, (struct sockaddr*)(&client_addr_info_struct), &size_of_sockaddr_in)) == -1) {  // 服务器阻塞, 直到接受到客户连接
			fprintf(stderr, "ERR accept():%s\n\a", strerror(errno));
			exit(1);
		}
 
		fprintf(stderr, "Got connection from %s\n", inet_ntoa(client_addr_info_struct.sin_addr)); // 网络地址 转换成 字符串
		if ((read_got_bytes_nr = read(server_session_socket, buffer, 1024)) == -1) {
			fprintf(stderr, "ERR read():%s\n", strerror(errno));
			exit(1);
		}
		buffer[read_got_bytes_nr] = '\0';
		printf("Server received %s\n", buffer); /* 这个对话服务已经结束 */
		close(server_session_socket); /* 下一个 */
	}
 
	/* 结束通讯 */
	close(local_listen_socket);
	exit(0);
}

查看代码客户端

#include <stdlib.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <unistd.h>
#include <arpa/inet.h> 
 
#define portnumber 3333
 
int main(int argc, char* argv[]) {
	int local_socket;
	char buffer[1024];
	struct sockaddr_in server_addr;
	struct hostent* host;
 
	if (argc != 2) {
		fprintf(stderr, "Usage:%s hostname \a\n", argv[0]);
		exit(1);
	}
 
	/* 使用hostname查询host 名字 */
	if ((host = gethostbyname(argv[1])) == NULL) {
		fprintf(stderr, "ERR gethostbyname\n");
		exit(1);
	}
 
	/* 客户程序开始建立 local_socket描述符 */
	if ((local_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // AF_INET:Internet;SOCK_STREAM:TCP
		fprintf(stderr, "ERR socket:%s\a\n", strerror(errno));
		exit(1);
	}
 
	/* 客户程序填充服务端的资料 */
	bzero(&server_addr, sizeof(server_addr)); // 初始化,置0
	server_addr.sin_family = AF_INET;          // IPV4
	server_addr.sin_port = htons(portnumber);  // (将本机器上的short数据转化为网络上的short数据)端口号
	server_addr.sin_addr = *((struct in_addr*)host->h_addr); // IP地址
 
	/* 客户程序发起连接请求 */
	if (connect(local_socket, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr)) == -1) {
		fprintf(stderr, "ERR connect:%s\a\n", strerror(errno));
		exit(1);
	}
 
	/* 连接成功了 */
	printf("Please typein a string:\n");
 
	/* 读取和发送数据 */
	fgets(buffer, 1024, stdin);
	write(local_socket, buffer, strlen(buffer));
	
	/* 结束通讯 */
	close(local_socket);
	exit(0);
}

编译执行

 

修改服务器为多线程模式

客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>//TODO: 某些头文件
#define MAX_BUFFER 1024

int main(int argc, char* argv[]) {
    int  local_socket;
    int  return_code;
    char send_and_receive_buffer[MAX_BUFFER];
    int  port_number;
    int  nr_bytes_read;

    static struct sockaddr_in server_sockaddr;

    //客户端运行需要给出具体的连接地址和端口 
    if (argc != 3) {
        printf("Usage: %s server_ip_address port_number \n", argv[0]);
        return 1;
    }

    //获得输入的端口
    port_number = atoi(argv[2]);

    //创建套节字用于客户端的连接
    local_socket = socket(PF_INET, SOCK_STREAM, 0);//TODO: 调用socket
    if (local_socket < 0) {//TODO: 如果错误
        perror("ERR socket.");
        return 1;
    }

    //填充关于服务器的套节字信息
    memset(&server_sockaddr, 0, sizeof(server_sockaddr));
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_addr.s_addr = inet_addr(argv[1]);
    server_sockaddr.sin_port = htons(port_number);

    //连接指定的服务器 
    return_code = (local_socket,(struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr));//TODO: 调用connect
    if (return_code == -1) {//TODO: 如果错误
        perror("ERR connect.");
        close(local_socket);
        return 1;
    }

    memset(send_and_receive_buffer, 0, MAX_BUFFER);
    //用户输入信息后,程序将输入的信息通过套接字发送给服务器 ,然后调用read函数从服务器中读取发送来的信息。
    //输入“#”退出 
    while (1) {
        //TODO: 12345 改为自己的学号,此处不要修改write调用和STDOUT_FILENO参数!
        write(STDOUT_FILENO, "Type in a string for 201930310050:", strlen("Type in a string for 201930310050:"));
        nr_bytes_read = read(STDOUT_FILENO, send_and_receive_buffer, MAX_BUFFER);//TODO: 调用read, 从STDIN_FILENO读
        if (nr_bytes_read > 0)
            write(local_socket, send_and_receive_buffer, nr_bytes_read);//TODO: 调用write,写socket
        nr_bytes_read = read(local_socket, send_and_receive_buffer, nr_bytes_read);//TODO: 调用read,读socket
        if (nr_bytes_read > 0)
            printf("Message form server: %s\n", send_and_receive_buffer);
        if (send_and_receive_buffer[0] == '#')
            break;
        //提示:刚才几句忽略了某些可能的错误,你能加上么?
    }
    close(local_socket);
    return 0;
}

 

服务端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include <ctype.h>
#include <stdlib.h>//TODO: 某些头文件
#define MAX_BUFFER 1024

void* thread_function(void* arg_array) {
    int nr_bytes_read, char_index;
    char receive_and_send_buffer[MAX_BUFFER];
    int* p_arg = (int*)arg_array;
    int thread_session_socket = *p_arg;
    printf("thread_session_socket = %d\n", thread_session_socket);
    while (1) {
        nr_bytes_read = read(thread_session_socket, receive_and_send_buffer, MAX_BUFFER);//TODO: 尝试从socket读取
        if (receive_and_send_buffer[0] == '#')//TODO: 如果错误,或没能从socket读取字符
            break;
        printf("Message from client(%d): %s\n", nr_bytes_read, receive_and_send_buffer);
        for (char_index = 0; char_index < nr_bytes_read; char_index++)
            receive_and_send_buffer[char_index] = toupper(receive_and_send_buffer[char_index]);//TODO: 转换为大写
        write(thread_session_socket, receive_and_send_buffer, char_index);//TODO: 返回给客户端
    }
    close(thread_session_socket);//TODO: 关闭socket
    return 0;
}

int main(int argc, char* argv[]) {
    socklen_t size_of_client_sockaddr;
    pthread_t tid;
    int listen_socket;
    int session_socket;
    int return_code;
    int port_number;

    struct sockaddr_in client_remote_sockaddr;
    struct sockaddr_in server_local_sockaddr;

    //服务器端运行时要给出端口信息,该端口为监听端口 
    if (argc != 2) {
        printf("Usage:%s port_number \n", argv[0]);
        return 1;
    }

    //获得输入的端口 
    port_number = atoi(argv[1]);

    //创建套接字用于服务器的监听 
    listen_socket = (PF_INET, SOCK_STREAM, 0);//TODO: socket()
    if (listen_socket < 0) {//TODO: 如果出错
        perror("ERR socket.");
        return 1;
    }

    //填充关于服务器的套节字信息
    memset(&server_local_sockaddr, 0, sizeof(server_local_sockaddr));
    server_local_sockaddr.sin_family = AF_INET;
    server_local_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_local_sockaddr.sin_port = htons(port_number);

    //将服务器和套节字绑定
    return_code = bind(listen_socket, (struct sockaddr *)&server_local_sockaddr, sizeof(server_local_sockaddr));//TODO: 调用bind绑定地址和端口(提供服务的位置)
    if (return_code == -1) {//TODO: 如果出错
        perror("ERR bind.");
        close(listen_socket);//TODO: 关闭监听socket
        return 1;
    }

    //监听指定端口,连接5个客户端 
    return_code = (listen_socket, 5);//TODO: 请求监听、提供服务
    if (return_code == -1) {//TODO: 如果出错
        perror("ERR listen.");
        close(listen_socket);//TODO: 关闭监听socket
        return 1;
    }

    //对每个连接来的客户端创建一个线程,单独与其进行通信。
    //首先调用read函数读取客户端发送来的信息,将其转换成大写后发送回客户端,#退出。
    while (1) {
        size_of_client_sockaddr = sizeof(client_remote_sockaddr);
        //TODO: 12345 改为自己的学号,此处不要修改write调用和STDOUT_FILENO参数!
        write(STDOUT_FILENO, "Listening & Accepting for 201930310050 ...\n", strlen("Listening & Accepting for 201930310050 ...\n"));
        return_code = accept(listen_socket, (struct sockaddr *)&client_remote_sockaddr, &size_of_client_sockaddr);//TODO: 调用accept阻塞,接到客户机时返回 session_socket
        if (return_code < 0) {//TODO: 如果出错
            if (errno == EINTR) continue;
            else {
                perror("ERR accept(): cannot accept client connect request");
                close(listen_socket);//TODO: 关闭socket
                return 1;
            }
        }
        printf("session_socket = %d\n", session_socket); //打印建立连接的客户端产生的套节字
        return_code = pthread_create(&tid, NULL, thread_function, &session_socket);//TODO: 调用 pthread_create,将 session_socket传递给 thread_function
        if (return_code == -1) {//TODO: 如果出错
            perror("ERR pthread_create()");
            close(listen_socket);//TODO: 关闭一个socket
            close(session_socket);//TODO: 关闭另一个socket
            return 1;
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值