라즈베리파이에서 서버를 열어 android에게 메시지를 받는것을 해보았다.
< C SERVER >
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#define MAXLINE 511
#define MAX_SOCK 1024 // 솔라리스의 경우 64
char *EXIT_STRING = "exit"; // 클라이언트의 종료요청 문자열
char *START_STRING = "Connected to chat_server \n";
// 클라이언트 환영 메시지
int maxfdp1; // 최대 소켓번호 +1
int num_user = 0; // 채팅 참가자 수
int num_chat = 0; // 지금까지 오간 대화의 수
int clisock_list[MAX_SOCK]; // 채팅에 참가자 소켓번호 목록
char ip_list[MAX_SOCK][20]; //접속한 ip목록
int listen_sock; // 서버의 리슨 소켓
// 새로운 채팅 참가자 처리
void addClient(int s, struct sockaddr_in *newcliaddr);
int getmax(); // 최대 소켓 번호 찾기
void removeClient(int s); // 채팅 탈퇴 처리 함수
int tcp_listen(int host, int port, int backlog); // 소켓 생성 및 listen
void errquit(char *mesg) { perror(mesg); exit(1); }
time_t ct;
struct tm tm;
void *thread_function(void *arg) { //명령어를 처리할 스레드
int i;
printf("명령어 목록 : help, num_user, num_chat, ip_list\n");
while (1) {
char bufmsg[MAXLINE + 1];
fprintf(stderr, "\033[1;32m"); //글자색을 녹색으로 변경
printf("server>"); //커서 출력
fgets(bufmsg, MAXLINE, stdin); //명령어 입력
if (!strcmp(bufmsg, "\n")) continue; //엔터 무시
else if (!strcmp(bufmsg, "help\n")) //명령어 처리
printf("help, num_user, num_chat, ip_list\n");
else if (!strcmp(bufmsg, "num_user\n"))//명령어 처리
printf("현재 참가자 수 = %d\n", num_user);
else if (!strcmp(bufmsg, "num_chat\n"))//명령어 처리
printf("지금까지 오간 대화의 수 = %d\n", num_chat);
else if (!strcmp(bufmsg, "ip_list\n")) //명령어 처리
for (i = 0; i < num_user; i++)
printf("%s\n", ip_list[i]);
else //예외 처리
printf("해당 명령어가 없습니다.help를 참조하세요.\n");
}
}
int main(int argc, char *argv[]) {
struct sockaddr_in cliaddr;
char buf[MAXLINE + 1]; //클라이언트에서 받은 메시지
int i, j, nbyte, accp_sock, addrlen = sizeof(struct
sockaddr_in);
fd_set read_fds; //읽기를 감지할 fd_set 구조체
pthread_t a_thread;
if (argc != 2) {
printf("사용법 :%s port\n", argv[0]);
exit(0);
}
// tcp_listen(host, port, backlog) 함수 호출
listen_sock = tcp_listen(INADDR_ANY, atoi(argv[1]), 5);
//스레드 생성
pthread_create(&a_thread, NULL, thread_function, (void *)NULL);
while (1) {
FD_ZERO(&read_fds);
FD_SET(listen_sock, &read_fds);
for (i = 0; i < num_user; i++)
FD_SET(clisock_list[i], &read_fds);
maxfdp1 = getmax() + 1; // maxfdp1 재 계산
if (select(maxfdp1, &read_fds, NULL, NULL, NULL) < 0)
errquit("select fail");
if (FD_ISSET(listen_sock, &read_fds)) {
accp_sock = accept(listen_sock,
(struct sockaddr*)&cliaddr, &addrlen);
if (accp_sock == -1) errquit("accept fail");
addClient(accp_sock, &cliaddr);
send(accp_sock, START_STRING, strlen(START_STRING), 0);
ct = time(NULL); //현재 시간을 받아옴
tm = *localtime(&ct);
write(1, "\033[0G", 4); //커서의 X좌표를 0으로 이동
printf("[%02d:%02d:%02d]", tm.tm_hour, tm.tm_min, tm.tm_sec);
fprintf(stderr, "\033[33m");//글자색을 노란색으로 변경
printf("사용자 1명 추가. 현재 참가자 수 = %d\n", num_user);
fprintf(stderr, "\033[32m");//글자색을 녹색으로 변경
fprintf(stderr, "server>"); //커서 출력
}
// 클라이언트가 보낸 메시지를 모든 클라이언트에게 방송
for (i = 0; i < num_user; i++) {
if (FD_ISSET(clisock_list[i], &read_fds)) {
num_chat++; //총 대화 수 증가
nbyte = recv(clisock_list[i], buf, MAXLINE, 0);
if (nbyte <= 0) {
removeClient(i); // 클라이언트의 종료
continue;
}
buf[nbyte] = 0;
// 종료문자 처리
if (strstr(buf, EXIT_STRING) != NULL) {
removeClient(i); // 클라이언트의 종료
continue;
}
// 모든 채팅 참가자에게 메시지 방송
for (j = 0; j < num_user; j++)
send(clisock_list[j], buf, nbyte, 0);
printf("\033[0G"); //커서의 X좌표를 0으로 이동
fprintf(stderr, "\033[97m");//글자색을 흰색으로 변경
printf("%s", buf); //메시지 출력
fprintf(stderr, "\033[32m");//글자색을 녹색으로 변경
fprintf(stderr, "server>"); //커서 출력
}
}
} // end of while
return 0;
}
// 새로운 채팅 참가자 처리
void addClient(int s, struct sockaddr_in *newcliaddr) {
char buf[20];
inet_ntop(AF_INET, &newcliaddr->sin_addr, buf, sizeof(buf));
write(1, "\033[0G", 4); //커서의 X좌표를 0으로 이동
fprintf(stderr, "\033[33m"); //글자색을 노란색으로 변경
printf("new client: %s\n", buf);//ip출력
// 채팅 클라이언트 목록에 추가
clisock_list[num_user] = s;
strcpy(ip_list[num_user], buf);
num_user++; //유저 수 증가
}
// 채팅 탈퇴 처리
void removeClient(int s) {
close(clisock_list[s]);
if (s != num_user - 1) { //저장된 리스트 재배열
clisock_list[s] = clisock_list[num_user - 1];
strcpy(ip_list[s], ip_list[num_user - 1]);
}
num_user--; //유저 수 감소
ct = time(NULL); //현재 시간을 받아옴
tm = *localtime(&ct);
write(1, "\033[0G", 4); //커서의 X좌표를 0으로 이동
fprintf(stderr, "\033[33m");//글자색을 노란색으로 변경
printf("[%02d:%02d:%02d]", tm.tm_hour, tm.tm_min, tm.tm_sec);
printf("채팅 참가자 1명 탈퇴. 현재 참가자 수 = %d\n", num_user);
fprintf(stderr, "\033[32m");//글자색을 녹색으로 변경
fprintf(stderr, "server>"); //커서 출력
}
// 최대 소켓번호 찾기
int getmax() {
// Minimum 소켓번호는 가정 먼저 생성된 listen_sock
int max = listen_sock;
int i;
for (i = 0; i < num_user; i++)
if (clisock_list[i] > max)
max = clisock_list[i];
return max;
}
// listen 소켓 생성 및 listen
int tcp_listen(int host, int port, int backlog) {
int sd;
struct sockaddr_in servaddr;
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd == -1) {
perror("socket fail");
exit(1);
}
// servaddr 구조체의 내용 세팅
bzero((char *)&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(host);
servaddr.sin_port = htons(port);
if (bind(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind fail"); exit(1);
}
// 클라이언트로부터 연결요청을 기다림
listen(sd, backlog);
return sd;
}
< Android Client >
안드로이드 클라이언트는 python-android_EX2 어플을 이용하였다.
[activity_main.xml]
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15px"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/linearLayout">
<!--android:layout_centerInParent="true"-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="IP :"
android:textSize="25dp"
tools:text="IP :" />
<EditText
android:id="@+id/ip_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/connect_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="CONNECT" />
</LinearLayout>
<TextView
android:id="@+id/show_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25dp" />
</LinearLayout>
</RelativeLayout>
[MainActivity.java]
package com.example.androidpython;
import android.os.Handler;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button connect_btn; // ip 받아오는 버튼
EditText ip_edit; // ip 에디트
TextView show_text; // 서버에서온거 보여주는 에디트
// 소켓통신에 필요한것
private String html = "";
private Handler mHandler;
private Socket socket;
private DataOutputStream dos;
private DataInputStream dis;
private String ip = "175.223.3.110"; // IP 번호
private int port = 8080; // port 번호
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
connect_btn = (Button)findViewById(R.id.connect_btn);
connect_btn.setOnClickListener(this);
ip_edit = (EditText)findViewById(R.id.ip_edit);
show_text = (TextView)findViewById(R.id.show_text);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.connect_btn: // ip 받아오는 버튼
connect();
}
}
// 로그인 정보 db에 넣어주고 연결시켜야 함.
void connect(){
mHandler = new Handler();
Log.w("connect","연결 하는중");
// 받아오는거
Thread checkUpdate = new Thread() {
public void run() {
// ip받기
String newip = String.valueOf(ip_edit.getText());
// 서버 접속
try {
socket = new Socket(newip, port);
Log.w("서버 접속됨", "서버 접속됨");
} catch (IOException e1) {
Log.w("서버접속못함", "서버접속못함");
e1.printStackTrace();
}
Log.w("edit 넘어가야 할 값 : ","안드로이드에서 서버로 연결요청");
try {
dos = new DataOutputStream(socket.getOutputStream()); // output에 보낼꺼 넣음
dis = new DataInputStream(socket.getInputStream()); // input에 받을꺼 넣어짐
dos.writeUTF("안드로이드에서 서버로 연결요청");
} catch (IOException e) {
e.printStackTrace();
Log.w("버퍼", "버퍼생성 잘못됨");
}
Log.w("버퍼","버퍼생성 잘됨");
// 서버에서 계속 받아옴 - 한번은 문자, 한번은 숫자를 읽음. 순서 맞춰줘야 함.
try {
String line = "";
int line2;
while(true) {
line = (String)dis.readUTF();
line2 = (int)dis.read();
Log.w("서버에서 받아온 값 ",""+line);
Log.w("서버에서 받아온 값 ",""+line2);
}
}catch (Exception e){
}
}
};
// 소켓 접속 시도, 버퍼생성
checkUpdate.start();
}
}
[AndroidManifest.xml]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidpython">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
※ 오류 주의 ※
C서버 컴파일 할때 기존의 [ gcc Cserver.c -o Cserver ] 하게 되면 pthread.h 를 포함했는데도 undefined reference to `pthread_create' 에러가 난다. 따라서 [ gcc -o Cserver Cserver.c -lpthread] 이런식으로 컴파일 해주면 정상적으로 실행파일이 만들어진다.
만들어진 뒤에 파일을 실행해보면 아래와 같이 뜬다. 이때 안드로이드에서 메시지를 보내주면 아래와 같이 "hello" , "junseop" 과 같이 메시지가 정상적으로 출력이 되는것을 볼 수 있다.
안드로이드에서 메시지 입력하는 부분이다.
'Socket' 카테고리의 다른 글
소켓통신으로 받은 데이터 mysql 서버에 저장하기 (python으로 쿼리문 이용) (0) | 2020.09.14 |
---|---|
소켓통신으로 실시간 메모장 데이터 보내기 (python) (2) | 2020.07.29 |
C (win) - Android (6) | 2020.03.02 |
라즈베리파이 (python) - android (0) | 2020.02.24 |
라즈베리파이(리눅스) - win <C 언어> (0) | 2020.02.24 |