Windows Server 2008 원격 데스크톱 연결에 멀티세션 허용하기 컴쟁이이야기

Many times, you’ll find it necessary to have multiple connections to a Remote Desktop server, and by default, Windows Server 2008, Windows Server 2003, Window Vista, Windows XP, etc. limit the number of Remote Desktop Connections to 1 user.

On Windows Server 2008, the Restrict Terminal Services users to a single remote session Group Policy setting determines whether you can connect to your existing physical console session. When this option is enabled, when logging on by default, you will be restricted to a single remote session.

This setting is available in the Computer Configuration\Administrative Templates\Windows Components\Terminal Services\Terminal Server\Connections node of the Local Group Policy Editor. You can also configure this setting in Terminal Services Configuration. The Restrict each user to a single session setting appears in Edit settings in the General section.

If you would like to have multiple simultaneous sessions for the same user, please uncheck this setting or set this group policy as Disabled.

For more information regarding Changes to remote administration in Windows Server 2008, please refer to the following KB article:

Changes to remote administration in Windows Server 2008

http://support.microsoft.com/kb/947723



Local Group Policy Editor 여는 방법 : 시작->실행->Gpedit.msc 입력


SQL Server T-SQL 에서 Update 시 inner join 사용하기 컴쟁이이야기


Update T
set col=S.col
from TargetTable T inner join SourceTable S
on T.keycol=S.keycol


SQL Server Stored Procedure 예제 - 있으면 Update, 없으면 Insert 컴쟁이이야기

SQL Server에서 Stored Procedure를 만들고 C#에서 호출하는 간단한 예제이다.


CREATE PROCEDURE [dbo].[SP_DAUM_INSUPD_PRODUCT]
@Seq int, @Name nvarchar(50), @ChargeType nchar(1), @ExpAmt int, @RegUser nvarchar(50), @RegDate datetime
WITH 
EXECUTE AS CALLER
AS
BEGIN
  DECLARE @Cnt int
  SELECT @Cnt = COUNT(*)  FROM ATAProductMaster WHERE Seq = @Seq
  IF( @Cnt > 0 )
  BEGIN
    UPDATE ATAProductMaster
       SET [Name] = @Name,
       ChargeType = @ChargeType,
       ExpAmt = @ExpAmt,
       RegUser = @RegUser,
       RegDate = @RegDate
     WHERE Seq = @Seq
  END
  ELSE
  BEGIN
    INSERT INTO ATAProductMaster(Seq,[Name],ChargeType,ExpAmt,RegUser,RegDate)
    VALUES(@Seq,@Name,@ChargeType,@ExpAmt,@RegUser,@RegDate)
  END
END




        public void InsUpd(SqlConnection conn)
        {
            ProcName = "SP_DAUM_INSUPD_PRODUCT";
            SqlCommand cmd = new SqlCommand(ProcName,conn);
            cmd.CommandType = CommandType.StoredProcedure;

            SqlParameter param = cmd.Parameters.Add("@Seq", SqlDbType.Int);
            param.Value = Seq;

            param = cmd.Parameters.Add("@Name",SqlDbType.NVarChar,50);
            param.Value = Name;

            param = cmd.Parameters.Add("@ChargeType", SqlDbType.NVarChar, 1);
            param.Value = ChargeType;

            param = cmd.Parameters.Add("@ExpAmt", SqlDbType.Int);
            param.Value = ExpAmt;

            param = cmd.Parameters.Add("@RegUser", SqlDbType.NVarChar, 50);
            param.Value = RegUser;

            param = cmd.Parameters.Add("@RegDate", SqlDbType.DateTime);
            param.Value = DateTime.Parse(ConvertDateTimeString(RegDate));

            cmd.ExecuteNonQuery();
        }


C# Database 사용 컴쟁이이야기

* C#에서 Database 를 사용할 때에는 사용하려는 Database의 종류에 따라 다음과 같은 클래스를 쓰게 된다.

- Database가 SQL Server인 경우: System.Data.SqlClient
- 그 외 Database인 경우: System.Data.OleDb - Access나 기타 다른 Database를 사용할 경우


* CommandBuilder 란 무엇인가?

Database를 사용해서 어느 테이블에서 특정 row들에 대한 조작을 하려고 할때 다음과 같이 코드를 사용할 수 있다:

sql = "SELECT * FROM TABLE_A";
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = new SqlCommand(sql, connection);
                DataSet pm = new DataSet();
                adapter.Fill(pm, "TABLE_A");

이렇게 하면, DataSet 인 pm에는 TABLE_A의 모든 row가 지정된다. 그리고 DataSet 내에 "TABLE_A"라는 테이블 이름으로 테이블을 하나 만들고 거기에 sql로 얻어진 row들을 채워 놓게 된다. 이제 조작을 하면 되는데...

위의 sql 에는 WHERE절이 없다. 물론 WHERE절을 사용해서 DataSet을 만들어도 되지만, 어떤 경우에는 그러지 못할 수도 있기 때문에 일단 DataSet을 만든 후 WHERE절을 대신하는 Filter를 사용할 수도 있다.

                DataTable table = pm.Tables["TABLE_A"];
                DataRow[] rows = table.Select("Seq = 9");

TABLE_A에 "Seq"라는 필드가 있다고 가정하면, 위에서 얻어진 rows 라는 배열은 Seq=9 를 만족하는 row들만 가지고 있게 된다.
그럼 이제, 이 rows의 특정 row의 값을 바꾸어 보자.

rows[0]["RegDate"] = DateTime.Parse("2010/03/01T22:10:25");

위의 코드는 Seq=9를 만족하는 row들 중 첫째 row에 값을 변경하는 코드이다.

이렇게 변경하고 나면 DataSet pm 안에 있는 DataTable table 의 row들 중 Seq=9를 만족하는 row의 첫째 row인 rows[0]["RegDate"] 의 값이 변경되었다. 하지만, 아직 이것은 Database 서버에 있는 테이블이 변경된 것은 아니다.

Table을 변경하려면 다음과 같이 해주어야 한다:
                adapter.Update(table);

그런데, 이렇게 하면 다음과 같은 에러가 발생한다:
Error Msg: 수정된 행을 포함하여 DataRow 컬렉션을 전달하는 경우 업데이트하려면 올바른 UpdateCommand가 필요합니다.

왜냐하면, 프로그램이 서버에 있는 테이블을 update할 때 SQL을 어떻게 써줘야 하는지 알지 못하기 때문이다. 이 때, SqlCommandBuilder를 생성해 주면 이것이 UpdateCommand를 자동으로 만들어 준다.

SqlCommandBuilder cb = new SqlCommandBuilder(adapter);

자, 이제 아래의 예제와 같이 하면 되는 것이다:
*** 단, INSERT/DELETE 문을 사용하려면 adapter.InsertCommand, adapter.DeleteCommand를 설정해야만 한다 ***

                string sql = String.Format("SELECT * FROM {0} WHERE Seq=9", ConstDef.TB_PRODUCTMASTER);
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = new SqlCommand(sql, connection);
                adapter.UpdateCommand = new SqlCommand();
                SqlCommandBuilder cb = new SqlCommandBuilder(adapter);

                DataSet pm = new DataSet();
                adapter.Fill(pm, ConstDef.TB_PRODUCTMASTER);

                DataTable table = pm.Tables[ConstDef.TB_PRODUCTMASTER];
                DataRow[] rows = table.Select("Seq = 9");

                foreach (DataRow row in rows)
                {
                    string a = "20100201223526";
                    string s = String.Format("{0}/{1}/{2}T{3}:{4}:{5}",
                        a.Substring(0,4),
                        a.Substring(4,2),
                        a.Substring(6,2),
                        a.Substring(8,2),
                        a.Substring(10,2),
                        a.Substring(12,2));

                    row["RegDate"] = DateTime.Parse(s);

                    foreach (DataColumn column in table.Columns)
                    {
                        Console.Write(" {0}", row[column]);
                    }
                    Console.WriteLine(" ");
                }
                adapter.Update(table);


C# 파일 읽어서 그에 대한 MD5 출력 컴쟁이이야기

using System.Security.Cryptography;
...

        private string GetMD5OfFile(string filepath)
        {
            StringBuilder strMD5 = new StringBuilder();
            FileStream fs = new FileStream(filepath, FileMode.Open);
            byte[] byteResult = (new MD5CryptoServiceProvider()).ComputeHash(fs);
            fs.Close();

            for (int i = 0; i < byteResult.Length; i++)
            {
                strMD5.Append(byteResult[i].ToString("X2"));
            }

            return strMD5.ToString();
        }

MD5 signature는 총 128비트(16byte)의 길이가 된다.

C# HTTP File Download 컴쟁이이야기

using System;
using System.IO;
using System.Net;
using System.Web;
using System.Windows.Forms;

namespace HttpFileDown
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnDown_Click(object sender, EventArgs e)
        {
            try
            {
                string filename = tbURL.Text.Substring(tbURL.Text.LastIndexOf("/")+1);
                MessageBox.Show(this, filename, this.Text, MessageBoxButtons.OK);

                WebRequest http = WebRequest.Create(tbURL.Text);
                byte[] buf = new byte[32768];

                using (Stream input = http.GetResponse().GetResponseStream())
                {
                    using (FileStream output = new FileStream(filename, FileMode.CreateNew))
                    {
                        int bytesRead = 0;
                        while ((bytesRead = input.Read(buf, 0, buf.Length)) > 0)
                        {
                            output.Write(buf, 0, bytesRead);
                            output.Flush();
                        }
                        input.Close();

                        output.Flush();
                        output.Close();

                        MessageBox.Show(this, output.Name+"의 다운로드를 마쳤습니다.", this.Text, MessageBoxButtons.OK);
                    }
                }


            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, this.Text, MessageBoxButtons.OK);
            }
        }
    }
}


C# Transaction 처리를 위한 예제 컴쟁이이야기


public void TotalDBExecute()
{
    string updateSql = "UPDATE Categories SET CategoryName = 'Car' WHERE CategoryID = 10";
    string deleteSql = "DELETE Categories WHERE CategoryID = 10";
    string conn = @"Server=222.232.2.22;Database=test;user=testuser;password=1111;Integrated Security=SSPI;";

    using (SqlConnection con = new SqlConnection(conn))
    {
        con.Open();

        // SqlTrasaction 클래스의인스턴스생성
        SqlTransaction tran = con.BeginTransaction(); // 트랜잭션시작
        SqlCommand cmd = new SqlCommand();

        cmd.Connection = con;
        cmd.Transaction = tran; // 현재사용할트랜잭션객체지정

        try
        {
            cmd.CommandText = updateSql;// 쿼리지정
            cmd.ExecuteNonQuery(); // 실행

            cmd.CommandText = deleteSql;
            cmd.ExecuteNonQuery();

            tran.Commit(); // 트랜잭션commit
        }
        catch (Exception ex)
        {
            tran.Rollback(); // 에러발생시rollback
        }
    }
}


MS SQL Server 에서 날짜 및 시각 비교하는 SQL 컴쟁이이야기


다음은, HeartbeatTime 필드에 지금보다 5분 이전의 시간이 있는 리스트를 조회하는 SQL이다.

select dm_id, heartbeattime, DATEDIFF(Minute, heartbeattime, GetDate()) as diff
from ADeviceMonitor
where DATEDIFF(Minute, heartbeattime, GetDate()) > 5
order by DM_Id

DATEDIFF 함수는 시간 비교를 하는 내장함수이다.

DATEDIFF

Returns the number of date and time boundaries crossed between two specified dates.

Syntax

DATEDIFF datepart , startdate , enddate )

Arguments

datepart

Is the parameter that specifies on which part of the date to calculate the difference. The table lists dateparts and abbreviations recognized by Microsoft® SQL Server™.

DatepartAbbreviations
Yearyy, yyyy
quarterqq, q
Monthmm, m
dayofyeardy, y
Daydd, d
Weekwk, ww
Hourhh
minutemi, n
secondss, s
millisecondms


PC에서 랜카드 두개 이상 쓸때 라우팅하기 컴쟁이이야기

<출처:http://x1210.tistory.com/437>


2개의 NIC에 서로 다른 route를 설정하는 방법

아래의 경우(먼저 linux route 명령어 참조)는 사실 default gateway(리눅스로 구성된..)를 만지고 있는 사람이 쓰게 된다. 
그럼 게이트가 2개일때 User(대부분의 User가 Windows 시스템을 쓴다는 가정하에...)에서는 또는 default gateway가 윈도우 시스템이라면 게이트를 이동시킬 수 없는 것인가?

물론 Windows에서도 이동시킬 수 있다.


먼저 자신의 routing table을 보려면

 

       route print  

 

명령어를 이용하면 된다. 그리고 라우팅 추가는

 

       route add 211.218.150.200 mask 255.255.255.255 192.168.0.2

 

하면 되고, 윈도우에서는 뒤에 -p옵션을 주면 컴퓨터를 리붓하더라도 없어지지 않는다.

 

그러면... default gateway가 있음에도 불구하고 다른 경로를 통해서 트래픽이 나가게 된다.

 

        User                      Default gateway                    www.naver.com
     192.168.0.3                   192.168.0.1                                 ↑ ↓
         ↑↓                                                                          ↑ ↓
         ↑↓                                                                          ↑ ↓
         ↑↓                                                                          ↑ ↓
         ↑ → → → → → → → → →  gate2 → → → → → → → →  ↓
          ← ← ← ← ← ← ← ← 192.168.0.2← ← ← ← ← ←  ← ← ←  

 

route 명령어 설명은 아래 help파일 참조...

route

로컬 IP 라우팅 테이블에서 항목을 표시하거나 변경합니다. 매개 변수 없이 route를 사용하면 도움말을 표시합니다.

구문

route [-f[-p[Command [Destination[mask Netmask[Gateway[metric Metric]] [if Interface]]

매개 변수

-f
호스트 경로(네트마스크가 255.255.255.255인 경로), 루프백 네트워크 경로(대상이 127.0.0.0이고 네트마스크가 255.0.0.0인 경로), 또는 멀티캐스트 경로(대상이 224.0.0.0이고 네트마스크가 240.0.0.0인 경로)가 아닌 모든 항목의 라우팅 테이블을 제거합니다. 이 매개 변수를 addchangedelete와 같은 명령 중 하나와 함께 사용하면 명령을 실행하기 전에 테이블을 제거합니다.
-p
add 명령과 함께 사용하면 TCP/IP 프로토콜이 시작될 때마다 지정된 경로가 레지스트리에 추가되고 IP 라우팅 테이블을 초기화하는 데 사용됩니다. 기본적으로, TCP/IP 프로토콜이 시작되면 추가된 경로가 보존되지 않습니다. print 명령과 함께 사용하면 영구 경로의 목록이 표시됩니다. 다른 모든 명령은 이 매개 변수를 무시합니다. 영구 경로는 레지스트리 위치HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\PersistentRoutes에 저장됩니다.
Command
실행할 명령을 지정합니다. 다음 표는 유효한 명령을 표시합니다.
명령용도
add   경로를 추가합니다.
change   기존 경로를 수정합니다.
delete   경로를 제거합니다.
print   경로를 인쇄합니다.
Destination
경로의 네트워크 대상을 지정합니다. 대상은 네트워크 주소의 호스트 비트가 0으로 설정된 IP 네트워크 주소나 호스트 경로의 IP 주소 또는 기본 경로 0.0.0.0일 수 있습니다.
mask Netmask
네트워크 대상과 함께 사용하는 네트마스크(서브넷 마스크)를 지정합니다. 서브넷 마스크는 IP 네트워크 주소의 해당 서브넷 마스크를 사용할 수도 있고, 호스트 경로인 255.255.255.255나 기본 경로인 0.0.0.0을 지정할 수도 있습니다. 서브넷 마스크를 지정하지 않으면 255.255.255.255를 사용합니다. 경로를 정의할 때 대상과 서브넷 마스크 사이의 관계 때문에 대상을 해당 서브넷 마스크보다 자세하게 지정할 수 없습니다. 다시 말하면, 서브넷 마스크의 해당 비트가 0인 경우 대상에서 비트를 1로 설정할 수 없습니다.

 

예제

IP 라우팅 테이블의 전체 내용을 표시하려면 다음과 같이 입력합니다.

 

route print

 

IP 라우팅 테이블에서 10.으로 시작하는 경로를 표시하려면 다음과 같이 입력합니다.

 

route print 10.*

 

기본 게이트웨이 주소인 192.168.12.1에 기본 경로를 추가하려면 다음과 같이 입력합니다.

 

route add 0.0.0.0 mask 0.0.0.0 192.168.12.1

 

서브넷 마스크 255.255.0.0과 다음 홉 주소 10.27.0.1과 함께 대상 10.41.0.0에 경로를 추가하려면 다음과 같이 입력합니다.

 

route add 10.41.0.0 mask 255.255.0.0 10.27.0.1

 

서브넷 마스크 255.255.0.0과 다음 홉 주소 10.27.0.1과 함께 대상 10.41.0.0에 영구 경로를 추가하려면 다음과 같이 입력합니다.

 

route -p add 10.41.0.0 mask 255.255.0.0 10.27.0.1

 

서브넷 마스크 255.255.0.0, 다음 홉 주소 10.27.0.1 그리고 비용 메트릭 7과 함께 대상 10.41.0.0에 경로를 추가하려면 다음과 같이 입력합니다.

 

route add 10.41.0.0 mask 255.255.0.0 10.27.0.1 metric 7

 

인터페이스 색인 0x3을 사용하여 서브넷 마스크 255.255.0.0, 다음 홉 주소 10.27.0.1과 함께 대상 10.41.0.0에 경로를 추가하려면 다음과 같이 입력합니다.

 

route add 10.41.0.0 mask 255.255.0.0 10.27.0.1 if 0x3

 

서브넷 마스크 255.255.0.0과 대상 10.41.0.0의 경로를 삭제하려면 다음을 입력합니다.

 

route delete 10.41.0.0 mask 255.255.0.0

 

IP 라우팅 테이블에서 10.으로 시작하는 경로를 모두 삭제하려면 다음과 같이 입력합니다.

 

route delete 10.*

 

대상이 10.41.0.0이고 서브넷 마스크가 255.255.0.0인 경로의 다음 홉 주소를 10.27.0.1에서 10.27.0.25로 변경하려면 다음과 같이 입력합니다.

 

route change 10.41.0.0 mask 255.255.0.0 10.27.0.25


무엇을, 어떻게 Open 할 것인가... 컴쟁이이야기

IBM은 PC를 개발하면서 개방형 구조를 지향했었다.

CPU, Memory, ROM BIOS, Main Board, Video Board, Sound 등의 부품들의 SPEC은 IBM의 주도하에 공개가 되었고 이에 따라 다양한 Vendor들이 출현하게 되어 PC 부품에서부터 완성품까지 제조하게 된다. IBM PC Compatible, 즉, IBM PC 호환 기종이란 말은 IBM이 개발한 PC와 규격이 호환됨을 말한다. Compaq이란 회사는 IBM PC 호환기종의 Desktop PC를 만들어 팔아서 대박을 쳤던 회사이다 - 나중에 HP에 합병되면서 이제는 사라졌지만.

Dell 이란 회사도 IBM PC 호환기종을 만들어 팔면서 커 온 회사이다. 물론 HP나 다른 PC제조사와는 달리 Online으로 소비자가 원하는 사양을 선택하면 그에 맞게 조립해서 배달해주는 사업모델로 성공을 거두었다. 기존의 제조사들은 한정된 숫자의 모델만을 팔고 있어서 소비자들의 선택권이 그만큼 없었기 때문에 Dell의 사업모델은 소비자들에게 크게 어필했었다...물론 미국에서 그랬다는 것이고...

그런데, 지금 IBM은 PC를 만들지 않는다. PC를 처음 기획하고, 설계하고, 제조하고, 판매하고 운영체제까지 만들어서 공급했던 IBM이 PC사업을 결국 포기한 것 처럼 보이는 것이다. 한 때 한국의 LG전자와 PC생산 계약을 맺어 LG전자가 IBM 상표를 달고 Original IBM PC의 자격으로 PC를 팔 수 있었다. 이 사업은 한동안 잘 되었었는데 결국 LG전자마저 수익성을 맞추지 못했는지 IBM은 중국에 있는 Lenovo라는 업체에 PC사업부문을 매각하기에 이른다. 이것이 IBM이 PC사업을 포기하는 것인지, 경영의 효율성을 높이기 위한 것인지는 차치하고라도, IBM과 PC시장에서 자웅을 겨루던 다른 회사와 확연히 비교되기 때문에 생각을 머무르게 한다. 바로 Apple 이다.

Apple은 HW와 OS를 모두 자신들이 만든다. HW 기술사양을 공개하지도 않는다. 공개하더라도 다른 PC제조사가 Apple 호환 PC를 만들지 못한다. 소송도 소송이려니와 우선 사람들이 Apple Clone이 있어도 잘 사려고 들지 않는다. 글쎄, 뭐랄까, 사람들이 비싸더라도 별다방가서 커피를 마시는 이유랑 비슷하다고 해야하나...? 커피를 마시는 것이 아닌 스타벅스라는 브랜드를 소비하는 것처럼, Apple이라는 브랜드를 사는 것이지 "그것과 같은 사양의 PC"를 원하는 것은 아니니까 말이다. 사실, Apple PC도 이제는 Intel CPU를 쓰기 때문에 성능에 있어서는 IBM 호환기종과 크게 다르지 않은게 사실이고, 같은 사양의 Apple과 IBM PC를 비교해보면, Apple이 훨씬...비싸다. 그렇다. 사람들이 굳이 Mac을 사는 것은 Apple이라는 브랜드 때문이지 Mac이 가진 HW, SW적인 성능이 가격대비 훌륭하기 때문은 아니라는 것이다. 그러니 Clone이 나온들 성공하겠는가...

Apple은 폐쇄적이다. Clone PC, 즉 호환기종을 용납하지 않는다. HW와 OS에 대한 전권을 쥐고 휘두르며 성장해 왔다. 결과는 IBM 호환기종과의 시장 점유율 대결에서 언제나 졌다. '진다'라는 표현보다는 '상대도 안된다' 라는 표현이 더 적합할 것이다. 그런데, 그렇게 시장에서 성공을 거둔 IBM PC를 만든 IBM은 PC 사업을 '접었고', Apple은 아직도 승승장구 하고 있다. 누가 무엇을 잘못했고 누가 무었을 잘했는가...?

1