Linux初使用
前言
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, ©Fd);
}
} /* 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);
}