TCP UDP IP 网络编程

查看while源代码

服务器代码

#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);
}

编译并在Ubuntu运行

gcc server-while-tcp.c -o server-while-tcp
gcc client.c -o client

运行结果

服务器
服务器端
客户端
客户端端

修改服务器为多线程模式

服务器代码

#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 <arpa/inet.h>
#include <stdlib.h>
#include <ctype.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 = recv(thread_session_socket, receive_and_send_buffer, MAX_BUFFER, 0);//TODO: 尝试从socket读取
        if (nr_bytes_read == -1)//TODO: 如果错误,或没能从socket读取字符
            break;
        printf("Message from client(%d): %s\n", nr_bytes_read, receive_and_send_buffer);
	char *p;	
	for (p = receive_and_send_buffer; *p != '\0'; p++)
		*p = toupper(*p);        
	//TODO: 转换为大写
        write(thread_session_socket, receive_and_send_buffer, MAX_BUFFER); //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 = socket(AF_INET, SOCK_STREAM, 0);//TODO: socket()
    if (listen_socket == -1) {//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(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 201930310035 ...\n", strlen("Listening & Accepting for 201930310035 ...\n"));    
	session_socket = accept(listen_socket, (struct sockaddr*)&client_remote_sockaddr, &size_of_client_sockaddr);//TODO: 调用accept阻塞,接到客户机时返回 session_socket
        if (session_socket == -1) {//TODO: 如果出错
            if (errno == EINTR) continue;
            else {
                perror("ERR accept(): cannot accept client connect request");
                close(session_socket);//TODO: 关闭socket
                return 1;
            }
        }
        printf("session_socket = %d\n", session_socket); //打印建立连接的客户端产生的套节字
        return_code = pthread_create(&tid, NULL, thread_function, (void*)&session_socket);//TODO: 调用 pthread_create,将 session_socket传递给 thread_function
        if (return_code != 0) {//TODO: 如果出错
            perror("ERR pthread_create()");
            close(session_socket);//TODO: 关闭一个socket
            close(listen_socket);//TODO: 关闭另一个socket
            return 1;
        }
    }
    return 0;
}

客户端代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.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(AF_INET, SOCK_STREAM, 0);//TODO: 调用socket
    if (local_socket == -1) {//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 = connect(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 201930310035:", strlen("Type in a string for 201930310035:"));
        nr_bytes_read = read(STDIN_FILENO, send_and_receive_buffer, MAX_BUFFER);//TODO: 调用read, 从STDIN_FILENO读
        if (nr_bytes_read > 0)
            write(local_socket, send_and_receive_buffer, MAX_BUFFER);//TODO: 调用write,写socket
        nr_bytes_read = read(local_socket, send_and_receive_buffer, MAX_BUFFER);//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;
}

编译后运行结果

服务器
在这里插入图片描述
客户端
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值