前言

linux的学习之路漫长且困难,就目前我作为一个这学期刚开始系统接触(我们专业上专业课)【23.03】的计算机专业学生来说,困难不仅体现在要脱离windows图形化界面的习惯性操作,还体现在需要熟悉命令行的各种操作,特别是代码编辑以及代码编译,更不用说配置linux环境中所需要遇到的各种非工程的问题,所以想要学习好Linux系统,就必须要求既要了解原理,更要动手实践。

socket网络编程

需要掌握的技术

socket API、多线程编程(pthread库的使用)、读写操作、各种库中数据结构的使用(如:sockaddr_in、fd_set等)

技术样例

概述

以下示例运用socket编程制作了一个简易的TCP服务端及TCP客户端

服务端

运用说明
  • 技术运用

整个代码实现了一个简单的TCP服务端:

- 创建一个socket并绑定到本地端口4321 - 监听socket的连接请求 - 当有新连接时,创建新线程 Accept并处理 - 主线程使用select监听连接的socket - 当socket有数据时,创建新线程 Message处理接收和发送数据 - 记录连接的客户端地址信息,用于显示是哪个客户端发送的信息 - 可以同时与多客户端进行通信这是一个使用IO多路复用和多线程处理TCP客户端连接的简单示例。

代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
​
#define PORT 4321  
#define MAX_QUE_CONN_NM 5  
#define MAX_SOCK_FD FD_SETSIZE
#define BUFFER_SIZE 1024
#define HOSTLEN 256  
​
pthread_mutex_t inset_mu, tmp_inset_mu;
fd_set inset, tmp_inset;
struct sockaddr_in scinf[100]; // client adder  
int sin_size = sizeof(struct sockaddr_in);
​
void *solve_first_accpet(void *sockfd) {
    int client_fd;
    struct sockaddr_in caddr;
    if ((client_fd = accept(*(int *)sockfd, (struct sockaddr *)&caddr,
                        &sin_size)) == -1) {
        perror("accept error");
        exit(1);
    }
    pthread_mutex_lock(&inset_mu);
    FD_SET(client_fd, &inset);
    pthread_mutex_unlock(&inset_mu);
    printf("New connection from %d(socket),%s\n", client_fd,
           inet_ntoa(caddr.sin_addr));
    if (client_fd < 100 && client_fd >= 0) {
        memcpy((void *)(&scinf[client_fd]), (void *)&caddr, sin_size);
    }
    return NULL;
}
​
void *solve_message(void *fd) {
    char buf[BUFFER_SIZE];
    int count;
    if ((count = recv(*((int *)fd), buf, BUFFER_SIZE, 0)) > 0) {
        if (*((int *)fd) >= 0 && *((int *)fd) < 100) {
            printf("Received a message from  %s:\t%s\n",
                   inet_ntoa(scinf[*((int *)fd)].sin_addr), buf);
        }
        if ((count = send(*((int *)fd), buf, strlen(buf), 0)) == -1) {
            perror("send");
            exit(1);
        }
    } else {
        if (*((int *)fd) >= 0 && *((int *)fd) < 100) {
            printf("client %s has left\n", inet_ntoa(scinf[*((int *)fd)].sin_addr));
        }
        memset((void *)(&scinf[*((int *)fd)]), 0, sin_size);
        close(*((int *)fd));
        pthread_mutex_lock(&inset_mu);
        FD_CLR(*((int *)fd), &inset);
        pthread_mutex_unlock(&inset_mu);
    }
    return NULL;
}
​
int main() {
    int sockfd, client_fd, count, fd;
    char buf[BUFFER_SIZE]; // receive and send buffer  
    struct sockaddr_in saddr, caddr;
    bzero((void *)scinf, sizeof(scinf));
​
    // create tcp socket  
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("create socket error");
        exit(1);
    }
​
    // setting socker configure  
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    // any address can access the server  
    saddr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(saddr.sin_zero), 8);
    int i = 1;
    /* 使得重复使用本地地址与套接字进行绑定 */
    // apply the setting and binding the file describer to the socket  
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr)) == -1) {
        perror("bind error");
        exit(1);
    }
    if (listen(sockfd, MAX_QUE_CONN_NM) == -1) {
        perror("listen error");
        exit(1);
    }
    printf("listening....\n");
    /*将调用socket函数的描述符作为文件描述符*/
    FD_ZERO(&inset);
    FD_SET(sockfd, &inset);
​
    while (1) {
      tmp_inset = inset;
      memset(buf, 0, sizeof(buf));
​
      /*调用select函数*/
      if (!(select(MAX_SOCK_FD, &tmp_inset, NULL, NULL, NULL) > 0)) {
        perror("select error");
        close(sockfd);
        exit(1);
      }
        
      for (fd = 0; fd < MAX_SOCK_FD; fd++) {
        pthread_t thread_id;
        if (FD_ISSET(fd, &tmp_inset) > 0) {
          if (fd == sockfd) { /* 服务端接收客户端的连接请求 */
            pthread_create(&thread_id, NULL, &solve_first_accpet, &sockfd);
          } else /* 处理从客户端发来的消息 */
          {
            int copyFd = fd;
            pthread_create(&thread_id, NULL, &solve_message, &copyFd);
          }
        } /* end of if FD_ISSET*/
      }   /* end of for fd*/
    }     /* end if while while*/
​
    close(sockfd);
    exit(0);
    }

客户端

运用说明
  • 技术运用

- 网络编程:使用TCP socket API创建网络客户端

- socket():创建socket

- connect():连接到服务器

- recv():接收数据

- send():发送数据

- gethostbyname():通过主机名获取IP地址

- inet_ntoa():IP地址转换为字符串

- strstr():查找字符串

- fgets():从stdin读取输入此外,代码也使用了sockaddr_in、hostent等数据结构。

  • 思想及过程解析

    整个代码实现了一个简单的TCP客户端:

    - 获取命令行参数指定的服务器主机名 - 通过gethostbyname()获取主机IP地址 - 创建socket并连接到服务器 - 进入循环,等待用户输入并发送到服务器 - 如果输入“exit”则退出循环 - 否则使用fgets()读取输入当前命令行用户输入内容并使用send()发送到服务器 - 使用recv()接收服务器返回的数据并显示 - 关闭socket

代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
​
#define PORT    4321
#define BUFFER_SIZE 1024
​
int main(int argc, char *argv[])
{
    int sockfd;
    char buf[BUFFER_SIZE];
    struct hostent *host;
    struct sockaddr_in saddr;
    
    if(argc < 2){//检查程序参数指定的服务器地址
        printf("USAGE: ./tcp_client Hostname(or ip address)\n");
        exit(1);
    }
    
    /*地址解析函数*/
    if ((host = gethostbyname(argv[1])) == NULL)
    {
        perror("gethostbyname");
        exit(1);
    }
    
    /*创建socket*/
    if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("socket");
        exit(1);
    }
    
    /*设置sockaddr_in 结构体中相关参数*/
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&(saddr.sin_zero), 8);
    
    /*调用connect函数主动发起对服务器端的连接*/
    if(connect(sockfd,(struct sockaddr *)&saddr, sizeof(struct sockaddr))== -1)
    {
        perror("connect");
        exit(1);
    }
    printf("connected to server!\n");
    
    while(1){
        bzero(buf,sizeof(buf));
        if(fgets(buf,BUFFER_SIZE,stdin)){
            if(strstr(buf,"exit")) break;
            if(send(sockfd,buf,strlen(buf),0)==-1){
                perror("send"); 
                exit(1);
            }
        }
        bzero(buf,sizeof(buf));
        if(recv(sockfd,buf,BUFFER_SIZE,0)==-1){
            perror("recv");
            exit(1);
        }else{
            printf("recevice form server:%s\n",buf);
        }
        
    }
    
    close(sockfd);
    exit(0);
}
​